Xamarin.Essentials - James Montemagno https://staging.montemagno.com/tag/xamarin-essentials/ https://staging.montemagno.com/content/images/2017/08/MonogramIcon_Orange_NoBuffer.png Xamarin.Essentials - James Montemagno https://staging.montemagno.com/tag/xamarin-essentials/ Hugo -- gohugo.io Tue, 13 Oct 2026 20:48:55 GMT 60 Enabling Users to Manage Subscriptions on iOS/Android https://staging.montemagno.com/enabling-users-to-manage-subscriptions-on-ios-android/ 632f2485ae7288003d7930aa Sat, 24 Sep 2022 16:01:17 GMT

You know I love talking about in app purchases and subscriptions. Due to my InAppBilling library I get all the "fun" updates when Apple or Google force developers to make changes to stay compliant. Upcoming is a nice large change from Google to push Android developers to use Billing v4 (which my library does!) and another is that we must now give users a way to easily manage and cancel their subscriptions. You may have recently seen this message from google:

We’re updating our Subscriptions policy to state that users must be able to easily cancel their subscriptions from within the app. {#we%E2%80%99re-updating-our-subscriptions-policy-to-state-that-users-must-be-able-to-easily-cancel-their-subscriptions-from-within-the-app}

What does this mean? What do I need to do Google?!?!! Well if you dig through the documentation you will see that you have to add a button to your purchase screen and also create a deep link to launch the store management screen. Let's see what this looks like in code for Xamarin and .NET MAUI apps:

public async Task ManageSubscription(string sku)
{
    var url = string.Empty;
    if (DeviceInfo.Platform == DevicePlatform.iOS)
    {
        url = "https://support.apple.com/HT202039";
    }
    else if (DeviceInfo.Platform == DevicePlatform.Android)
    {
        if (string.IsNullOrWhiteSpace(sku))
        {
            url = "https://play.google.com/store/account/subscriptions";
        }
        else
        {
            url = $"https://play.google.com/store/account/subscriptions?sku={sku}&package={AppInfo.PackageName}";
        }
    }

    if (string.IsNullOrWhiteSpace(url))
        return;

    await Browser.OpenAsync(url);
}

For iOS there isn't really a requirement or even an API to give users an easy way to manage subscriptions, but I figure why not.

For Android, there are two cases here:

  1. They don't have any non-expired subscriptions and you just launch the generic page.
  2. Your user has a non-expired subscriptions for your app, where expiryTime is in the future or autoRenewing is set to true. In this case you would want to open the subscription page to the very specific subscription for them to manage.

And, that is about it... Well actually you could simplify this entire process by creating a Manage App Subscriptions page on your website and linking to that. Then your code becomes:

await Browser.OpenAsync("https://refractored.com/manage-app-subscriptions");

This should also adhere to the app store guidelines and also you can add additional information. Cheers!

Checkout my videos on YouTube

]]>
How-to Integrate Helpful Support Emails in Mobile Apps with Insightful Data & Logs https://staging.montemagno.com/how-to-add-support-email-xamarin-apps/ 5ff7ed8f98406600382024eb Sun, 10 Jan 2021 00:46:44 GMT In the mad rush to finish my most recent app, My Cadence, I forgot to integrate an essential feature. A feature that enables my users to easily get a hold of me to report issues or request features, which is of course email support. Both Google Play and the App Store enable you to supply a support website and email, however this is a very lack luster experience for both you and your users. For your users they need to go open the app store, find your app, and then find the support button. For you, you get no information about the user's device, operating system, or even your app information. With just a few lines of code and Xamarin.Essentials, you can create a great experience for you and your users.

Information Filled Email

You could just put a simple "email support" button that opens the default email app, but let's think about how we could pre-populate it with helpful data. The first thing to think about is what you need about your user's device and your app that may be helpful. This includes things like version number, build number, operating system version, manufacturer, and model. With AppInfo and DeviceInfo APIs you can easily populate a string and generate an email with a few lines of code:

var builder = new StringBuilder();
builder.AppendLine($"App Version: {AppInfo.VersionString} | Build: {AppInfo.BuildString}");
builder.AppendLine($"OS: {DeviceInfo.Platform}");
builder.AppendLine($"OS Version: {DeviceInfo.VersionString}");
builder.AppendLine($"Manufacturer: {DeviceInfo.Manufacturer}");
builder.AppendLine($"Device Model: {DeviceInfo.Model}");
builder.AppendLine(string.Empty);
builder.AppendLine("Please let me know what issues you are having here:");

var message = new EmailMessage
{
    Subject = "My Cadence Support",
    To = new List<string>(new []{ "example@email.com" }),
    Body = builder.ToString(),
};

await Email.ComposeAsync(message);

Logger

This information is great, but you may want to additional logs and debug information. I regularly use System.Diagnostics.Debug.WriteLine() when debugging my app to print information, so I thought to myself... what if I enabled uses to gather all this debug information and sent it to me. So, I first created a setting that I could enable users to turn on and off with the Preferences API:

public static bool DebugMode
{
   get => Preferences.Get(nameof(DebugMode), false);
   set => Preferences.Set(nameof(DebugMode), value);
}

Then, I created a simple Logger class that I could think of that would simply append a line onto a StringBuilder in memory.

public static class Logger
{
    static Logger()
    {
        Init();
    }
    public static void Init()
    {
        DebugMode = Settings.DebugMode;
    }
    public static StringBuilder Log = new StringBuilder();
    public static bool DebugMode { get; set; }

    public static void AppendLine(string line)
    {
        Debug.WriteLine(line);
        if (!DebugMode)
            return;

        Log.AppendLine(line);
    }
}

While I do acknowledge that this may not be the "best" solution and there are tons of logging libraries out there, this one was very easy for me to implement in about 30 seconds.

Attach Logs to Email

Now, once I have the logs all setup, I can use Xamarin.Essentials again to attach the logs by writing the StringBuilder directly to a file in the cache and the send it my way!

var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss", CultureInfo.InvariantCulture);
var fn = $"log_{timestamp}.txt";
var file = Path.Combine(FileSystem.CacheDirectory, fn);
File.WriteAllText(file, Logger.Log.ToString());

message.Attachments.Add(new EmailAttachment(file));

Support email with device info and logs

This method has already helped me fine and fix a few bugs in my app and really streamlines communication with my users.

]]>
Asking for app reviews really works! It just takes 1 line of code! https://staging.montemagno.com/asking-for-app-reviews-really-works/ 5feb59b2875b7700393723b6 Tue, 29 Dec 2020 17:30:00 GMT

Last week I launched a new app called My Cadence for both iOS and Android. Even though it was a quick app that only took me a week or so to build and ship, I still followed my checklist of best practices to ensure as much success as I could. I of course did real world testing myself and with TestFlight users, I tested light and dark theme modes, and I even put in-app purchases in from the start! Even with all of this, there one line of code (using the Store Review Plugin) that can really make a difference for your app once it hits the app store, and that is asking users for reviews!

I know what you are thinking, but James everyone hates those pop ups asking for reviews. I agree with this statement, but only when the request for a review is not handled properly. For example, I got a request to review my banking application before I even logged in! That is annoying and you know that the users is going to cancel and never leave a review. So, when I implement app review requests, I try to be clever about it track a main action that the user is doing. Then after so many times I will prompt them for a review. In the case of My Cadence after 5 successful connections to a sensor when the user hits stop, I will prompt for a review.

The first thing I do is create a small preference that can track usage across sessions with Xamarin.Essentials Preferences:

public static int Usage
{
    get => Preferences.Get(nameof(Usage), 0);
    set => Preferences.Set(nameof(Usage), value);
 }

Preference to save an integer for usage stats

Then, when the user presses Stop to disconnect, I grab the count, increase it, check if it is the 5th time, and the request a review using the Store Review Plugin.

var count = Settings.Usage;
count++;
if(count == 5)
{
    CrossStoreReview.Current.RequestReview(false);
}
Settings.Usage = count;

Requesting Review after 5 times using the app

The best part is that this enables the users to not only give you a star rating, but also allows the user to write a full review on both iOS and Android!

iOS flow from start to finish
iOS flow from start to finish
Android review flow
Android review flow

Now, on to my findings! It really works! My app has been available for 1 week now and I already have 6 ratings and 5 full reviews!

App Store Connect with Reviews!
App Store Connect with Reviews!

It is still early days on Android, but I already have 2 ratings and a review, which is awesome! This method of asking specific users after they use specific features of your app a specific amount of time is a wonderful way of getting helpful reviews. The logic here is that users that are repeat users of the application must like it enough to keep using it and are more likely to leave a review. So far, this rings true, but of course you need to have an application that does what is advertised.

I hope that this was helpful and that you take a look at putting in-app review requests into your app with this simple method.

iOS Screenshot from Apptentive

]]>
Building, testing, & shipping My Cadence for iOS in less than a week https://staging.montemagno.com/building-testing-shipping-my-cadence-for-ios-in-less-than-a-week/ 5fe12e796c10e100399f4eae Thu, 24 Dec 2020 17:43:57 GMT

My holiday hacking came early this year when I decided to build my own DIY spin bike. Soon, after a few rides, my mind started to swirl with app ideas of how to improve my ride. Then when Apple released Apple Fitness+ without support for displaying cadence information from sensors I knew I had to build an app. That app is My Cadence, which connects to a small cadence sensor and displays the current cadence. It is actually packed full of awesome features including custom themes, in-app purchases, store reviews, and more! Best of all it only took me around 12 hours to develop, test, and ship during the last week since I started it. So, today I want to walk through all of the features I put in and how I developed them.

Xamarin.Forms 5

With Xamarin.Forms 5 nearing stable release it was the first thing I updated to when I created the project. I leveraged a lot of new features such as embedded fonts, App Themes for Light/Dark mode, gradient brushes, geometry clipping, and a bunch more! I love how easy it is to react to the mode change on the user's device with a simple line of code.

 <Label Text="The following sensors have been tested with My Cadence."
        TextColor="{AppThemeBinding Dark=White, Light=Black}"/>

Here is a bit of the app themes in action:

Bluetooth

The cadence sensor broadcast over Bluetooth Low Energy. This was actually the first time that I had worked with Bluetooth and I leveraged the Plugin.BLE library to easily detect, connect, and read data from the device. I may upgrade to Shiny in the future, but this was the first one I grabbed and it has been working well.

I fell in love so much with Bluetooth that Frank and I recorded a full Merge Conflict podcast on it!

In-app Purchases

I decided to add a "Pro" mode into the application at the last minute. My thought was that the entire app would be free without restrictions, but to help support the app a user could upgrade to "Pro" to unlock custom theming in the app. I leveraged my InAppBilling plugin and had the upgraded implemented in about 30 minutes. Here is the code from the app if you are interested in how easy it is to implement.

const string productId = "pro";

async Task PurchasePro()
{
    if (IsBusy)
        return;

    IsBusy = true;

    try
    {
        if(Connectivity.NetworkAccess != NetworkAccess.Internet)
        {
            await DisplayAlert("No internet", "Please check internet connection and try purchase again.", "OK");
            return;
        }

        var connected = await CrossInAppBilling.Current.ConnectAsync();

        if (!connected)
        {
            await DisplayAlert("Unable to Connect", "Unable to connect to the app store, check your internet connectivity and try again.", "OK");
            return;
        }

        //check purchases
        var purchase = await CrossInAppBilling.Current.PurchaseAsync(productId, ItemType.InAppPurchase);

        if (purchase == null)
        {
            return;
        }
        else if (purchase.State == PurchaseState.Purchased)
        {
            Settings.IsPro = true;
            SetPro();
            return;
        }

        throw new InAppBillingPurchaseException(PurchaseError.GeneralError);

    }
    catch (InAppBillingPurchaseException purchaseEx)
    {
        var message = string.Empty;
        switch (purchaseEx.PurchaseError)
        {
            // figureout error message
        }

        if (string.IsNullOrWhiteSpace(message))
            return;

        Console.WriteLine("Issue connecting: " + purchaseEx);
        await DisplayAlert("Uh Oh!", message, "OK");
    }
    catch (Exception ex)
    {
        // Handle Exception
    }
    finally
    {
        await CrossInAppBilling.Current.DisconnectAsync();
        IsBusy = false;
    }
}

Store Reviews

On thing that I always forget to put in my app is a way to prompt the user to review the application. Another one of my random plugins comes in to save the day, the Store Review Plugin! In the settings screen I added a button to review the app:

void TextCellReview_Tapped(System.Object sender, System.EventArgs e)
{
    CrossStoreReview.Current.OpenStoreReviewPage("1545730360");
}

Then I also decided to do a prompt for the user to review after they use the app 5 times successfully.

var count = Settings.Usage;
count++;
if(count == 5)
{
    CrossStoreReview.Current.RequestReview(false);
}
Settings.Usage = count;

Custom Controls

I surprisingly I only used 1 custom control in the entire app and it was available directly on NuGet! This control was an advanced color picker and I landed on one built by Dalton Spillman. It is fantastic as it allows full fidelity of color selection and has a built in ViewModel that works great for getting and setting colors.

You can find the code to the control on GitHub.

Xamarin.Essentials

Wow, Xamarin.Essentials continues to make me super productive over and over and over again. I leveraged:

  • Version Tracking
  • Preferences
  • Connectivity
  • MainThread
  • and more!

Every time I thought I was going to need to write some custom code Xamarin.Essetials was there.

CI & CD

In one of my late night code sessions I decided to setup App Center Build/Distribute on the app and I am so glad I did. It took seconds to setup and I had things automatically building, versioning, and distributing to TestFlight in under 10 minutes!

App Center Build & Distribute

More!

There is a lot more that went into this application outside of just the code. I generated a full website and app screenshots from http://apptoolkit.io/, created an app icon from EasyAppIcon.com, & had some amazing tester on Twitter using TestFlight.  

Thanks Dan for Testing!
Thanks Dan for Testing!

Available Now & OSS Code Soon

It is now available in the App Store completely for free! Soon the app is coming to Android as well as I just need to tweak a few Android specific settings and get it through the review process.

If you are looking to get into spinning checkout my full blog on building a DIY spin bike.

Finally, I do plan on putting the full source code online, so be sure to check back when I write up a blog on it.

]]>
Optimizing Xamarin Apps & Libraries with the Linker https://staging.montemagno.com/optimizing-xamarin-apps-libraries-with-the-linker/ 5b398519a4b3dd00bfb1cdad Thu, 13 Jun 2019 20:30:00 GMT The Xamarin linker is one of the most important pieces of technology when building mobile apps, that no one knows exists. It does all of the hard work for you, stripping out all unused code from libraries that you include in your apps. It should only be used in release mode as it takes extra time to compile your app, but means that your app will be smaller:

Capture

The linker has specific implementations for both iOS and Android and is completely customizable.

Linking for App Developers

Before I get to how to optimize libraries, let me go through when the linker actually kicks in and how app developers use it.

For the large majority of developers, all you need to know is turn on Link SDK Assemblies Only when your app is in Release (it is the default). This will only look to link and remove libraries that ship with Xamarin.iOS and Xamarin.Android. The other option is Link All Assemblies, which will look at every assembly and library that your app uses and attempt to link them. I always recommend Link SDK Assemblies Only as it is the safest and easiest to understand. The difference between the two in terms of application size is usually minimal and often when linking everything you can get in trouble as the linker can be aggressive and will remove more than needed, especially if you use reflection.

The linker is pretty customizable and you can tell it to ignore specific libraries in the project settings. For instance if you are building an app with Xamarin.Forms you probably want to leave all of Xamarin.Forms in there so you could add some flags to iOS and Android:

// iOS
 <MtouchExtraArgs>--linkskip=Xamarin.Forms.Platform.iOS --linkskip=Xamarin.Forms.Platform --linkskip=Xamarin.Forms.Core --linkskip=Xamarin.Forms.Xaml --linkskip=Samples</MtouchExtraArgs>

// Android
<AndroidLinkSkip>Xamarin.Forms.Platform.Android;Xamarin.Forms.Platform;Xamarin.Forms.Core;Xamarin.Forms.Xaml;Samples;FormsViewGroup;</AndroidLinkSkip>

You can learn more about this on the iOS and Android documentation.

Linking for Library Creators

For library creators it is super important to understand how the linker works, and how you can optimize your libraries for app developers. You never know when a developer may flip on Link All Assemblies or when the linker may get aggressive and link away some of your library.

Preserve Everything

There are instances where you are creating a library and always want to ensure that NOTHING is ever removed regardless of the linker setting. The [Preserve (AllMembers = true)] can be added to any class in your iOS or Android library and then the linker will ignore that class. I like to always test my library in Link ALL Assemblies mode and if something goes wrong and gets removed then I slap this on the class.

Linker Safe

Sometimes it is really important that unused code in your library is properly removed. This is where the [assembly:LinkerSafe] attribute comes in to inform the linker to include this library in ALL linking modes. This is cool because you are telling the linker that you have done the work to test your library and want it to be treated as if it was part of Xamarin.iOS or Xamarin.Android.

A great example of this is Xamarin.Essentials which is completely optimized with this property. It means that if you only use the Compass then that will be the ONLY chunk of code that is included in the app. This library was built and structured with the linker in mind with very little overlapping code, and each feature is implemented in it's own class.

When I started to look at my other cross-platform libraries I noticed that the PermissionsPlugin was starting to cause people some submission issues on iOS. This was because the library itself checks all sorts of different permissions and since I didn't optimize it for the linker, Apple was detecting usage of the APIs and started to require the info.plist to add permissions that weren't actually being used.

So, I turned on the [assembly:LinkerSafe] and thought everything was going to be fine... until I packaged up an app, extracted the dll from the final package and put it into dotPeek on Windows and found out that NOTHING was getting linked out:

AllItems

This fuzzy photo (sorry about that) shows that even though my code was only requesting one permission, all of the code was still there. Well, let's look at the code:

public async Task<Dictionary<Permission, PermissionStatus>> RequestPermissionsAsync(params Permission[] permissions)
{
    var results = new Dictionary<Permission, PermissionStatus>();
    foreach (var permission in permissions)
    {
        if (results.ContainsKey(permission))
            continue;

        switch (permission)
        {
            case Permission.Calendar:
                results.Add(permission, await RequestEventPermission(EKEntityType.Event));
                break;
            case Permission.Camera:
                results.Add(permission, await RequestAVPermissionStatusAsync(AVMediaType.Video));
                break;
            case Permission.Contacts:
                results.Add(permission, await RequestContactsPermission());
                break;
            case Permission.LocationWhenInUse:
            case Permission.LocationAlways:
            case Permission.Location:
                results.Add(permission, await RequestLocationPermission(permission));
                break;
            case Permission.MediaLibrary:
                results.Add(permission, await RequestMediaLibraryPermission());
                break;
            case Permission.Microphone:
                results.Add(permission, await RequestAVPermissionStatusAsync(AVMediaType.Audio));
                break;
            case Permission.Photos:
                results.Add(permission, await RequestPhotosPermission());
                break;
            case Permission.Reminders:
                results.Add(permission, await RequestEventPermission(EKEntityType.Reminder));
                break;
            case Permission.Sensors:
                results.Add(permission, await RequestSensorsPermission());
                break;
            case Permission.Speech:
                results.Add(permission, await RequestSpeechPermission());
                break;
        }

        if (!results.ContainsKey(permission))
            results.Add(permission, PermissionStatus.Granted);
    }

    return results;
}

At first, you are probably thinking like me that the linker obviously knows what Permission my app is passing in, so it would remove any case that wasn't being used. Nope, that linker is smart, but not that smart. If I call this method at all, the linker will include any method that is being used inside of it. This means that I would need to re-structure the entire library to make it linker safe. James Clancey came up with the amazing idea of breaking the mega method into smaller individual classes that compartmentalized functionality.

This is where the BasePermission came in:

public class BasePermission
{
    protected Permission permission;
    public BasePermission(Permission permission)
    {
        this.permission = permission;
    }

    public virtual Task<PermissionStatus> CheckPermissionStatusAsync() =>
#if __IOS__
        throw new NotImplementedException();
#else
        CrossPermissions.Current.CheckPermissionStatusAsync(permission);
#endif

    public virtual async Task<PermissionStatus> RequestPermissionAsync()
    {
#if __IOS__
        throw new NotImplementedException();
#else
        var results = await CrossPermissions.Current.RequestPermissionsAsync(permission);
        if (results.ContainsKey(permission))
            return results[permission];

        return PermissionStatus.Unknown;
#endif
    }
}

The code looks a little funny, but it has two virtual methods that on iOS have no implementation at all and must be implemented in a parent class. This means that when the linker sees the BasePermission on iOS it wont have any implementation and will get removed. Now, all I needed to do is create classes for each of the permissions such as this:

public class CameraPermission : BasePermission
{
    public CameraPermission() : base(Permission.Camera)
    {

    }
#if __IOS__
    public override Task<PermissionStatus> CheckPermissionStatusAsync() =>
        Task.FromResult(PermissionsImplementation.GetAVPermissionStatus(AVFoundation.AVMediaType.Video));

    public override Task<PermissionStatus> RequestPermissionAsync() =>
        PermissionsImplementation.RequestAVPermissionStatusAsync(AVFoundation.AVMediaType.Video);
#endif
}

public class ContactsPermission : BasePermission
{
    public ContactsPermission() : base(Permission.Contacts)
    {

    }
#if __IOS__
    public override Task<PermissionStatus> CheckPermissionStatusAsync() =>
        Task.FromResult(PermissionsImplementation.ContactsPermissionStatus);

    public override Task<PermissionStatus> RequestPermissionAsync() =>
        PermissionsImplementation.RequestContactsPermission();
#endif
}

On iOS you can see that I am calling into specific implementations for each permission check and request. This now means that even though the library contains 15 different permission classes if the calling app only uses 1 of them then it will be the only code that is included and there will be no more issues with app store submissions!

Some

The linker is pretty complex, but pretty awesome when you figure out how to optimize for it. If you are a library creator I encourage you to flip on this flag for your iOS and Android projects and give it a test drive. This will ensure that you always know if your library will work regardless of any linker setting.

Be sure to read through all the documentation I linked to and also check out [Jon Douglas' blog]9https://www.jon-douglas.com/2017/09/22/linker-analyzer/) on using the linker analyzer for Android. You can also watch me struggle on my live stream from a month back where I figured out all of this.

]]>
Unique Device & App Installation Identifiers for Mobile Apps https://staging.montemagno.com/unique-device-id-for-mobile-apps/ 5c0ec14158056200bf4c259f Fri, 14 Dec 2018 20:00:00 GMT One common query I get all the time from developers is how to get a unique device or application identifier for their mobile app. After deciding to not include an API for this in Xamarin.Essentials we heard a lot of different use cases for it including:

  • Client/Server Communication
  • Login Validation
  • In-App Purchase Verification
  • Licensing Purposes

The next obvious question that you may have is why didn't we include it in Xamarin.Essentials if everyone seemed to want it? The answer is actually pretty straight forward: There is truly no consistent API across every platform to deliver an identifier that is unique to the device or application. On top of that developers all wanted to use the API a bit differently and in some cases make it a core part of processing transactions and that is something we don't want to get in the middle of.

So what can you do if you need this identifier? I would first stop and ask yourself if you can use some sort of User Identifier based on login information as this will ensure uniqueness that you control. If not then there are a few options to go down then trying to implement this.

Platform APIs Available

Each platform offers up something a bit different:

iOS: IdentifierForDevice

This API is pretty interesting as it will give you an alphanumeric string that uniquely identifies a device to the app's vendor. Based on your Bundle ID that you set Apple will return back the same identifier. The issue that I always found is that this can be spoofed by someone else setting a similar Bundle ID, but it is in general a decent API:

public string Id => UIDevice.CurrentDevice.IdentifierForVendor.AsString();

Android: Serial, getSerial & AndroidId

What a mess Android is as there are now three APIs that do similar things and even Google's documentation is all over the place on which to use!

A recommended route would be to attempt to grab the Serial number when available. In the world of Xamarin we will automatically figure out whether to use the static property or the new getSerial() method introduced in Android 9.0 (which requires the READ_PHONE_STATE). The issue here is that this can be modified via rooted devices and sometimes is not available on all hardware. This means that you need to attempt to figure out something else to use, which would be the ANDROID_ID. This is a unique number that is randomly generated when the device is setup, unless you are on API 26+ which is a number that is unique for each combination of app-signing key, user, and device.... As you can see now we have like 4 different identifiers here that all mean different things. If you went this route then your code would look like this:

string id = string.Empty;
public string Id
{
    get
    {
        if (!string.IsNullOrWhiteSpace(id))
            return id;

        id = Android.OS.Build.Serial;
        if(string.IsNullOrWhiteSpace(id) || id == Build.Unknown || id == "0")
        {
            try
            {
                var context = Android.App.Application.Context;
                id = Secure.GetString(context.ContentResolver, Secure.AndroidId);
            }
            catch(Exception ex)
            {
                Android.Util.Log.Warn("DeviceInfo", "Unable to get id: " + ex.ToString());
            }
        }

        return id;
    }
}

Before going down this route think about the implications here of your code and what you are trying to accomplish.

UWP: GetPackageSpecificToken or GetSystemIdForPublisher

We are in a tricky place for UWP as well depending on what we are trying to do. GetSystemIdForPublisher is very similar to what iOS does, but it may not be available in all versions of Windows. The next level is trying to grab a unique hardware identifier for the specific app with GetPackageSpecificToken. So in this case your code may look like:

string id = null;
public string Id
{
    get
    {

        if (id != null)
            return id;

        try
        {
            if (ApiInformation.IsTypePresent("Windows.System.Profile.SystemIdentification"))
            {
                var systemId = SystemIdentification.GetSystemIdForPublisher();

                // Make sure this device can generate the IDs
                if (systemId.Source != SystemIdentificationSource.None)
                {
                    // The Id property has a buffer with the unique ID
                    var hardwareId = systemId.Id;
                    var dataReader = Windows.Storage.Streams.DataReader.FromBuffer(hardwareId);

                    var bytes = new byte[hardwareId.Length];
                    dataReader.ReadBytes(bytes);

                    id = Convert.ToBase64String(bytes);
                }
            }

            if (id == null && ApiInformation.IsTypePresent("Windows.System.Profile.HardwareIdentification"))
            {
                var token = HardwareIdentification.GetPackageSpecificToken(null);
                var hardwareId = token.Id;
                var dataReader = Windows.Storage.Streams.DataReader.FromBuffer(hardwareId);

                var bytes = new byte[hardwareId.Length];
                dataReader.ReadBytes(bytes);

                id = Convert.ToBase64String(bytes);
            }

            if(id == null)
            {
                id = "unsupported";
            }

        }
        catch (Exception)
        {
            id = "unsupported";
        }

        return id;
    }
}

Just like Android you may get different results based on what you are looking for.

A True Cross-Platform Identifier

Now that we have seen each of the platforms and how inconsistent they are, let's talk about a better way of doing this. What most developers are attempting to do is ensure that when an application is installed they can get some unique identifier for that installation. If the app is transfered to a new phone then the identifier is the same, but if it is un-installed then a new identifier is generated. This can easily be accomplished by using Xamarin.Essentials Preferences API to save a Guid when your app starts if it doesn't exist yet. The code is super simple:

var id = Preferences.Get("my_id", string.Empty);
if(string.IsNullOrWhitespace(id))
{
   id = System.Guid.NewGuid().ToString();
   Preferences.Set("my_id", id);
}

This will ensure a unique identifier for the installed application and it is actually the same mechanism that App Center uses for their SDK across each platform! You should of course be aware that this Guid will sync across the user in some instances based on their device and account setup, which is probably what you want to have happen.

There you have it a full break down of each platform with the complexity that each offer. However, my recommendation remains that you should just generate a Guid and go from there :)

]]>
Upgrading to Xamarin.Essentials from Plugins https://staging.montemagno.com/upgrading-from-plugins-to-xamarin-essentials/ 5b3ee4e62a21a600bf2b04f5 Wed, 19 Sep 2018 18:52:00 GMT

Xamarin.Essentials is a new official library from Microsoft (that I worked on) that provides developers with over 30 cross-platform APIs for their mobile apps to tap into to access native features. Yes, this may sound similar to Plugins for Xamarin, but it is an evolution that is super optimized for each plaform, provides a consistent API, and is super crazy well documented. And of course it is completely open source on GitHub!

Essentials

Why Upgrade?

A lot of developers have asked me why they should upgrade their apps, that have been using Plugins for a long time, to Xamarin.Essentials. Here is my list:

  1. This is an OFFICIAL package from the Xamarin team at Microsoft.
  2. It is super optimized and Linker Safe!
  3. All of your favorite plugins are there.
  4. Only has 1 assembly to help boost your app's startup time.
  5. Super consistent API across all features.
  6. Because it is awesome and super well tested.

Also because I upgraded one of my apps and it reduced that app's assemblies from 47 to 35 and even reduced the application size from 22.7MB to 22.2MB! Because it is crazy optimized!

With Xamarin.Essentials we started over from the beginning and we were able to craft new APIs for features, which means it wasn't just a copy and paste of plugins out there. We re-thought each and every API, optimized them with new code paths for each platform, integrated unit tests and samples, and documented the heck out of them.

This means if you are moving from Plugins to Essentials there is a tiny bit of work you need to do.

Getting Started

We have a great Getting Started Guide that outlines everything you need to know on how to install the NuGet package that you need. Be aware that since it is a single NuGet package this means on Android we DO NOT automatically add permissions, so read the docs on each API to see what you need to add.

The APIs!

I will try to cover as many of my Plugins as I can, but I am sure I will miss some of them. Here we go!

Settings Settings Icon

My settings plugin was not only my first plugin that I ever created, but it also is my most popular plugin with over 1.4 Million NuGet installs over the years. Xamarin.Essentials replaces and upgrades it with the Preferences API. The APIs are extremely similar, but fixes a lot of small things that had been lingering around in my code. It is a very smooth transition as they both use the same default locations to store preferences and even allow for a specific name to be passed in.

Preferences API Access

  • Plugin: CrossSettings.Current.
  • Essentials: Preferences.

Preferences API

  • GetValueOrDefault -> Get
  • AddOrUpdateValue -> Set

The key difference here is that Set simply is a method void call and saves the value every time.

While the new preferences API saves to the same places it doesn't support all of the value types that the Settings Plugin does like decimals. I should have never supported these as I have to store them as string, but you can convert it easily like this:

public decimal MyDecimal
{
   get
  {
       var savedDecimal = Preferences.Get("mykey",  Convert.ToString(0, CultureInfo.InvariantCulture));
       return Convert.ToDecimal(savedDecimal, CultureInfo.InvariantCulture)
   }
   set { Preferences.Set("mykey", Convert.ToString(value, CultureInfo.InvariantCulture)); }
}

In addition to the core APIs you could also open the app settings page on the device. This can now be done in the AppInfo API of Essentials:

  • CrossSettings.Current.OpenAppSettings(); -> AppInfo.OpenSettings();

Connectivity Plugin connectivity-icon

Oh connectivity plugin... I love you! My second most popular plugin out there that has helped developers detect if there is an internet connection for several years. This is also an easy one as many of the APIs are packed into Xamarin.Essentials Connectivity API. There is a little bit of setup for permissions that you should do if you want to check connection types.

Connectivity API Access

  • Plugin: CrossConnectivity.Current.
  • Essentials: Connectivity.

Connectivity API

  • IsConnected -> NetworkAccess (now returns an enum)
  • ConnectionTypes -> Profiles
  • ConnectivityChanged & ConnectivityTypeChanged -> ConnectivityChanged (all rolled into 1 nice event)

So you can replace this:

if (!CrossConnectivity.Current.IsConnected)
{
    //we are offline
}

with this:

if (Connectivity.NetworkAccess != NetworkAccess.Internet)
{
    //we are offline
}

APIs that don't exist

  • IsReachable & IsRemoteReachable: You can read my full analysis on GitHub as to why we did not include this, but long story short I should have never included these. It really makes no sense to try to ping a site to see if it is there instead of just making the web request.
  • Bandwidth: Never really was a need and super inconsistent.

App Version / Version Tracking version-tracking-icon

I simply love our App Info API that gives you all sorts of info about your application. You may have been using other plugins such as DeviceInfo or VersionTracking, but you can just use AppInfo now:

App Info API Access

  • Essentials: AppInfo.

App Info API

  • Name
  • PackageName
  • VersionString
  • BuildString

There is also a new Version Tracking API that is nearly a 1 to 1 replacement for the old plugin.

Version Tracking API Access

  • Plugin: CrossVersionTracking.Current.
  • Essentials: VersionTracking.

Version Tracking API

  • Track(); -> Track();
  • IsFirstLaunchEver -> IsFirstLaunchEver
  • IsFirstLaunchForVersion -> IsFirstLaunchForCurrentVersion
  • IsFirstLaunchForBuild -> IsFirstLaunchForCurrentBuild
  • CurrentVersion -> CurrentVersion
  • PreviousVersion -> PreviousVersion
  • FirstInstalledVersion -> FirstInstalledVersion
  • VersionHistory -> VersionHistory
  • CurrentBuild -> CurrentBuild
  • PreviousBuild -> PreviousVersion
  • FirstInstalledBuild -> FirstInstalledBuild
  • BuildHistory -> BuildHistory

There are a lot of enhancements to this API including that it just uses the built-in Preferences API instead of serialization and file IO.

Share Plugin share-plugin

The share plugin kept growing and started to add tons of APIs for no good reason including opening the browser or setting text on the clipboard. Don't worry though because Xamarin.Essentials has a Data Transfer, Clipboard, and Browser API built right in!

API Access

  • Plugin: CrossShare.Current.
  • Essentials: Browser.
  • Essentials: Clipboard.
  • Essentials: DataTransfer. (this is Share)

Browser API

  • OpenBrowser -> OpenAsync
  • BrowserOptions -> BrowserLaunchType (no current open to specify colors)

Clipboard API

  • SetClipboardText -> SetText
  • NEW API -> GetTextAsync

Data Transfer API

  • Share -> RequestAsync
  • Pass in a ShareTextRequest which specifies text or a uri.

Device Information device-info-plugin

I loved to hate my Device Info plugin becaues it had a lot of great ideas and a lot of bad ideas such as the original implementation of ID and a bunch of other random methods. This has all been fixed with Xamarin.Essentials and the Device Information API, and the addition of the Device Display API that adds the ability to get information about the device's screen metrics, which is very nifty.

API Access

  • Plugin: CrossDeviceInfo.Current.
  • Essentials: DeviceInfo.

DeviceInfo API

  • Model -> Model
  • Manufacturer -> Manufacturer
  • DeviceName -> Name
  • Version -> VersionString
  • AppVersion -> Now: AppInfo.VersionString
  • AppBuild -> Now AppInfo.BuildString
  • Platform -> Platform
  • Idiom -> Idiom
  • IsDevice -> DeviceType

Geolocator geolocator-icon

Another popular plugin of mine that was originally brought over from the old Xamarin.Mobile code base. Xamarin.Essentials has built this API from the ground up on each platform and really thought about what developers need from this API. That is why there are actually two APIs that developers can use and a standardization on what a Location and Placemark is. So hopefully any other APIs could start using these. The first API is Geolocation which is how to get the cached location or current location on the device. Also Geocoding enables getting the Placemark for a Location. As of right now there is no tracking of location changes, but that is on the roadmap.

API Access

  • Plugin: CrossGeolocator.Current.
  • Essentials: Geolocation.
  • Essentials: Geocoding.

Geolocation API

  • GetLastKnownLocationAsync -> GetLastKnownLocationAsync
  • GetPositionAsync -> GetLocationAsync

Geocoding API

  • GetAddressesForPositionAsync -> GetPlacemarksAsync
  • GetPositionsForAddressAsync -> GetLocationsAsync

Text-to-Speech text-to-speech

My plugin has gone under a lot of changes over the year and I have learned a lot that went into the new Text-to-Speech API in Xamarin.Essentials.

API Access

  • Plugin: CrossTextToSpeech.Current.
  • Essentials: TextToSpeech.

Text to Speech API

  • Speak -> SpeakAsync
  • GetInstalledLanguages -> GetLocalesAsync

Vibratevibrate-icon

Another simple tranisition to the Vibrate API and even adds some new methods. For Android be sure to setup permissions correctly in your Android Manifest.

API Access

  • Plugin: CrossVibrate.Current.
  • Essentials: Vibration.

Vibration API

  • Vibration -> Vibrate
  • NEW API -> Cancel

Battery battery-plugin

When you need to check the battery status of the device the Battery API is for you! We also have a new Power API that will help you detect if the device is in power saver mode too!

API Access

  • Plugin: CrossBattery.Current.
  • Essentials: Battery.

Battery API

  • RemainingChargePercent -> ChargeLevel (0 to 1.0)
  • Status -> State
  • PowerSource -> PowerSource
  • BatteryChanged -> BatteryChanged

Wrapping Up

If you made it this far then I am impressed and want to give you a big hug! Xamarin.Essentials is pretty fantastic and I hope that you see how easy it is to change over from Plugins, that it has the ability to shrink your app size and help with startup time by reducing your total assemblies in your app.

One thing to note is that my plugins aren't going away any time soon. Many of them will go into maintenance mode as I push developers towards Xamarin.Essentials. The key difference is that many of my plugins for better or worse support a lot of different platforms that aren't supported yet in Essentials.

There are also some plugins that I plan on supporting long term as they provide functionality not yet in Essentials such as Permissions and Store Review or features that aren't planned at all such as IniApp Billing.

]]>
Multi-Targeting: Where Did My Files Go? Fixing .NET Core 2.1 SDK Issues https://staging.montemagno.com/multi-targeting-where-did-my-files-go-fixing-net-core-2-1-sdk-issues/ 5b7ade4acce53300bfd41234 Tue, 21 Aug 2018 20:45:00 GMT I love multi-targeting for creating libraries, and it is at the core of all of my plugins and Xamarin.Essentials. If you don't know what multi-targeting is don't worry as it is pretty rare that you would need to use it if you are creating apps. Most likely you will want to use multi-targeting in creating cross-platform libraries. In the past you would have to create multiple libraries for each target framework (such as iOS or Android) that you wanted to support if there was platform specific code. With the new SDK style projects you can set the TargetFrameworks property and specify as many as you would like.

Here is Xamarin.Essentials that supports iOS, Android, UWP, and .NET Standard 1.0/2.0:

<TargetFrameworks>netstandard1.0;netstandard2.0;Xamarin.iOS10;MonoAndroid71;MonoAndroid80;MonoAndroid81;uap10.0.16299</TargetFrameworks>

What is amazing is that all of your files can live together and can be conditionally compiled so only certain files get compiled for a specific platform:

<ItemGroup>
    <Compile Include="**\*.shared.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('netstandard')) ">
    <Compile Include="**\*.netstandard.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('uap10.0')) ">
    <Compile Include="**\*.uwp.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('MonoAndroid')) ">
    <Compile Include="**\*.android.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('Xamarin.iOS')) ">
    <Compile Include="**\*.ios.cs" />
</ItemGroup>

This allows super beautiful project structures:

Capture

It also means that you can conditionally compile your code too:

#if NETSTANDARD1_0
    // do something for .net standard 1.0
#elif __ANDROID__
    // else for Android
#else
    // anything else.
#endif

This all works thanks to Oren Novotny's amazing library MSBuild SDK Extras, which is easily configured by adding a nuget to all projects and adding a single import at the bottom of your csproj:

<ItemGroup>
    <PackageReference Include="MSBuild.Sdk.Extras" Version="1.6.47" PrivateAssets="All" />
    <Compile Include="**\*.shared.cs" />
</ItemGroup>
<Import Project="$(MSBuildSDKExtrasTargets)" Condition="Exists('$(MSBuildSDKExtrasTargets)')" />

Enter .NET Core SDK 2.1

Since this is sort of "unofficial" special tooling magic, sometimes things can break and recently when I installed .NET Core SDK 2.1 all my files went missing for iOS/Android/UWP. If we toggle on "show all files" they are totally there and get compiled but now intellisense stops working:

Missing

We reported it and worked with the .NET team on how to resolve it.

Fix 1: Include Workaround

The work around from the .NET team was to add an additional include at the bottom of our csproj that basically includes any .cs files and excludes defaults.

<ItemGroup>
    <PackageReference Include="MSBuild.Sdk.Extras" Version="1.6.47" PrivateAssets="All" />
    <Compile Include="**\*.shared.cs" />
</ItemGroup>
<ItemGroup>
    <None Include="**\*.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>
<Import Project="$(MSBuildSDKExtrasTargets)" Condition="Exists('$(MSBuildSDKExtrasTargets)')" />

Fix 2: Update to 1.6.47 of Sdk Extras & Project Type

I work extremely closely with Oren and whenever something happens with Essentials I freak out :) So when we had Fix 1 in place and working the next natural thing was to roll it into the official NuGet! Last week Oren did just this!

The only thing here is that we will want to ensure we upgrade to his new project type instead of using the PackageReference and Import. He outlines it great in his documentation.

In short instead of adding any nugets or imports we will replace:

<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">

with

<Project Sdk="MSBuild.Sdk.Extras/1.6.47">

Of course if you are using other types of files you may need to go back to Fix 1 to add them in manually, but his system is very smart. Be sure to report any bugs over to Oren on his GitHub repo.

]]>
Optimizing C# Struct Equality with IEquatable and ValueTuples https://staging.montemagno.com/optimizing-c-struct-equality-with-iequatable/ 5b746b2dcce53300bfd41229 Thu, 16 Aug 2018 19:00:42 GMT

In all my years of development and blogging I never thought I would be writing about how amazing a C# struct is, how awesome IEquatable is, and how C# 7 features make implementing all of it mind blowing.

In Xamarin.Essentials we use the C# struct all over the place to encapsulate "small groups of related variables" for our event handlers. They are groups of data that don't need to be created by the developers consuming the data and are only really used for reading the data.

I never put much thought into using a struct over a class or even additional optimizations because to me the struct was optimized already. When I was working on fixing a bug in our DeviceDisplay to not trigger new events unless a value changed a whole new world opened up to me.

Let's take the struct that we were using at the time:

public struct ScreenMetrics
{
    internal ScreenMetrics(double width, double height, double density, ScreenOrientation orientation, ScreenRotation rotation)
    {
        Width = width;
        Height = height;
        Density = density;
        Orientation = orientation;
        Rotation = rotation;
    }

    public double Width { get; set; }
    public double Height { get; set; }
    public double Density { get; set; }
    public ScreenOrientation Orientation { get; set; }
    public ScreenRotation Rotation { get; set; }
}

Mutable vs Immutable

The first issue we see here is that this struct is mutable in that you can actually change the data later on via the set properties. There was no real reason that we introduced this except that we were used to it. Since a developer should never really create one of these objects the setters should be removed and it should be marked as readonly to ensure that it is immutable, which is a pretty slick C# 7.2 feature:

public readonly struct ScreenMetrics
{
    internal ScreenMetrics(double width, double height, double density, ScreenOrientation orientation, ScreenRotation rotation)
    {
        Width = width;
        Height = height;
        Density = density;
        Orientation = orientation;
        Rotation = rotation;
    }

    public double Width { get; }
    public double Height { get; }
    public double Density { get; }
    public ScreenOrientation Orientation { get; }
    public ScreenRotation Rotation { get; }
}

Overriding Equals

Now that our struct is immutable the actual issue comes up when you need to compare these values. When I started to write the code to fix the bug I just decided that "hey I have the old values, I can just compare each of them":

 static void OnScreenMetricsChanged(ScreenMetricsChangedEventArgs e)
{
    if (currentMetrics.Width != e.Metrics.Width ||
        currentMetrics.Height != e.Metrics.Height ||
        currentMetrics.Density != e.Metrics.Density ||
        currentMetrics.Orientation != e.Metrics.Orientation ||
        currentMetrics.Rotation != e.Metrics.Rotation)
    {
        SetCurrent();
        ScreenMetricsChangedInternal?.Invoke(null, e);
    }
}

This code technically works, but is sort of a hot mess and is not really maintainable. Anyone using the library would have to write this code as well. The next logical step would be to just use .Equals on the entire metrics.

Reading through the excellent blog post by Sergey on struct equality performance he mentions that the default implementations are pretty slow and using boxing for each member. Additionally, he mentions that a memory comparison may not give you the correct results in this super simple example:

public struct MyDouble
{
    public double Value { get; }
    public MyDouble(double val) => Value = val;
}

double d1 = -0.0;
double d2 = +0.0;

// True
bool b1 = d1.Equals(d2);

//False
bool b2 = new MyDouble(d1).Equals(new MyDouble(d2));

So, this means we must override Equals!

public override bool Equals(object obj) =>
    (obj is ScreenMetrics metrics) && Equals(metrics);

public bool Equals(ScreenMetrics other) =>
    currentMetrics.Width.Equals(e.Metrics.Width) &&
    currentMetrics.Height.Equals(e.Metrics.Height) &&
    currentMetrics.Density.Equals(e.Metrics.Density) &&
    currentMetrics.Orientation.Equals(e.Metrics.Orientation) &&
    currentMetrics.Rotation.Equals(e.Metrics.Rotation);

Now we know for sure that if we call Equals, that our checks will work! However, we can do even better here with an awesome C# 7.3 feature called Tuple Equality! That is right, you can create a ValueTuple and simply compare them as they are super optimized, don't create any objects, and reduce this to a single line of code!

public bool Equals(ScreenMetrics other) =>
            (Width, Height, Density, Orientation, Rotation) == (other.Width, other.Height, other.Density, other.Orientation, other.Rotation);

Mind blown!!!

What about the HashCode

That is right! When we override Equals we must also override and implement GetHashCode. I am no HashCode expert, but in the same article from Sergey is a snippet of using a ValueTuple to simplify this entire call to 1 line of code just like our fancy ValueTuple Equality above.

public override int GetHashCode() =>
    (Height, Width, Density, Orientation, Rotation).GetHashCode();

This is really amazing code and works great for .NET Standard libraries. If you are in a .NET Core 2.1 application there is an even cooler way of doing this:

public override int GetHashCode() =>
    HashCode.Combine(Height, Width, Density, Orientation, Rotation);

I really hope this makes it to the next versions of .NET Standard as it really is discoverable.

More Optimized with IEquatable

Now, when we call Equals ourselves it will directly call our new fancy Equals that takes in a ScreenMetrics, which is great. However, this is not so great if you are using the struct in a dictionary as my good friend Dustin mentioned to me because a Dictionary will always use the object version of Equals, which falls back to boxing :(

Do not fear because if you simply implement IEquatable<T> the dictionary will use the strongly typed version! The nice thing is that we kind of actually already did this! So now we just have to do this:

public readonly struct ScreenMetrics : IEquatable<ScreenMetrics>
{
    //...
}

What about == and !=

Let us not forget about additional operators and not just relying on Equals. We can implement the == and != operators easily:

public static bool operator ==(ScreenMetrics left, ScreenMetrics right) =>
    Equals(left, right);

public static bool operator !=(ScreenMetrics left, ScreenMetrics right) =>
    !Equals(left, right);

Our Final Beautiful Struct

Here it is in all of it's glory:

public readonly struct ScreenMetrics : IEquatable<ScreenMetrics>
{
    internal ScreenMetrics(double width, double height, double density, ScreenOrientation orientation, ScreenRotation rotation)
    {
        Width = width;
        Height = height;
        Density = density;
        Orientation = orientation;
        Rotation = rotation;
    }
    public double Width { get; }
    public double Height { get; }
    public double Density { get; }
    public ScreenOrientation Orientation { get; }
    public ScreenRotation Rotation { get; }

    public static bool operator ==(ScreenMetrics left, ScreenMetrics right) =>
        Equals(left, right);

    public static bool operator !=(ScreenMetrics left, ScreenMetrics right) =>
        !Equals(left, right);

   public override bool Equals(object obj) =>
       (obj is ScreenMetrics metrics) && Equals(metrics);

    public bool Equals(ScreenMetrics other) =>
        (Width, Height, Density, Orientation, Rotation) == (other.Width, other.Height, other.Density, other.Orientation, other.Rotation);

    public override int GetHashCode() =>
        (Height, Width, Density, Orientation, Rotation).GetHashCode();
}

This also means that all of these cases work:

[Fact]
public void DeviceDisplay_Comparison_Equal()
{
    var device1 = new ScreenMetrics(
        width: 0,
        height: 0,
        density: 0,
        orientation: ScreenOrientation.Landscape,
        rotation: ScreenRotation.Rotation0);

    var device2 = new ScreenMetrics(
        width: 0,
        height: 0,
        density: 0,
        orientation: ScreenOrientation.Landscape,
        rotation: ScreenRotation.Rotation0);

    Assert.True(device1.Equals(device2));
    Assert.True(device1 == device2);
    Assert.False(device1 != device2);
    Assert.Equal(device1, device2);
    Assert.Equal(device1.GetHashCode(), device2.GetHashCode());
}

What if it was automated?

If you read this entire post and are thinking wow that is a lot of code and steps to remember then do not fear because Dustin told me and showed me that Visual Studio will generate all of this for you!!!!! Check this out:

Tune In To Merge Conflict

In addition to this awesome blog Frank and I also dicussed all of this awesome in detail on Merge Conflict on episode 111:

]]>
Resolving Android Support Library NuGet Installation Issues https://staging.montemagno.com/resolving-android-support-library-nuget-installation-issues/ 5b55f9fd982bf300bf388f92 Wed, 25 Jul 2018 19:15:00 GMT

Xamarin.Essentials has to be my favorite project that I have worked on at Xamarin so far. Bringing together over 30 native platform specific APIs into a single API that creates a linker safe and optimized library that so far has really delighted developers. I did some surveys last week and everyone could not stop saying positive things about the library, which is awesome! One thing stood out though that seems to be causing issues, which is how NuGet handles installing and updating Android Support Libraries.

I recently wrote an article outlining exactly what each of the Android API Levels means. It also highlights why you may see Version conflict detected when installing a library that depends on a support library and how to solve it. My friend Jon Dick took it a step farther and went into low level details on his blog and created an awesome video and we even added troubleshooting to our docs on how to solve this. I figure why not try again one more time on how to solve this if you are having issues installing Xamarin.Essentials, plugins, or other Xamarin.Android libraries.

Compile Against the Latest

Notice that I said COMPILE not Target! When we want to use newer Android Support Libraries we must ensure that we are compiling our android app against the correct version of Android. You can see what version of Android you must compile against to install a NuGet package for Android based on the dependency. Note that Xamarin.Essentials lists MonoAndroid,Version=8.1 aka Android 8.1 SDK 27.

Dependencies

As of today when you install Visual Studio 2017 you will receive Android 8.1 SDK, but if you have an older project you may have it compiling against an older version. Simply right click on your project and under Application you will see Compile using Android version: (Target Framework) and set it to Android 8.1.

Compile

Remember that this is the version of Android we use to COMPILE your application and has no effect over Minimum or Target Android versions, which live in the Android Manifest section of the project properties.

Resolving Version Conflict

I will not go too deep into why or how this is happening because Jon already did this in his post. Just remember that when you attempt to install Xamarin.Essentials or other packages that use Support Libraries you want to ensure that all of your Support Libraries are up to date and use the same version. In the case of my application I had Support version 27.0.2 installed, but Xamarin.Essentials required 27.0.2.1 of CustomTabs and Core.Utils which were not yet in my project so I received:

Version conflict detected for Xamarin.Android.Support.Compat. Reference the package directly from the project to resolve this issue.
 App10.Android -> Xamarin.Essentials 0.8.0-preview -> Xamarin.Android.Support.CustomTabs 27.0.2.1 -> Xamarin.Android.Support.Compat (= 27.0.2.1)
 App10.Android -> Xamarin.Android.Support.Design 27.0.2 -> Xamarin.Android.Support.Compat (= 27.0.2).

This error makes sense because I have 27.0.2 of a Support Library that depends on Support.Compat, but CustomTabs also depends on this library and needs 27.0.2.1 and NuGet doesn't know what to do.

There are a few solutions here:

Option 1: Update All NuGets First

If we know that Xamarin.Essentials needs 27.0.2.1 then just update all of your Support Libraries to this version.

Update

After doing this you will be able to install Xamarin.Essentials.

Option 2: Install Dependencies

One thing to do is not only update your Support Libraries, but also install any of the Support Library dependencies that the NuGet needs. You simply want to match the current version of the other support libraries first or update to 27.0.2.1 and then install both CustomTabs and Core.Utils.

Option 3: Update PackageReferences (My Favorite)

When all else fails or if you are like me and are tired of messing with the NuGet UI then you can just open up your Android projects .csproj and update the PackageReferences to ensure all support libraries are on 27.0.2.1 and also add the other libraries you need. Here is what mine looks like:

<PackageReference Include="Xamarin.Essentials" Version="0.8.0-preview" />
<PackageReference Include="Xamarin.Android.Support.Design" Version="27.0.2.1" />
<PackageReference Include="Xamarin.Android.Support.v7.AppCompat" Version="27.0.2.1" />
<PackageReference Include="Xamarin.Android.Support.v4" Version="27.0.2.1" />
<PackageReference Include="Xamarin.Android.Support.v7.CardView" Version="27.0.2.1" />
<PackageReference Include="Xamarin.Android.Support.v7.MediaRouter" Version="27.0.2.1" />
<PackageReference Include="Xamarin.Android.Support.Core.Utils" Version="27.0.2.1" />
<PackageReference Include="Xamarin.Android.Support.CustomTabs" Version="27.0.2.1" />

When you re-load your project the NuGet packages will re-load and everything will be happy.

Go Code

That is it! We have resolved all of our issues and you can continue coding! Leave comments below if you have any other questions.

]]>