Xamarin - James Montemagno https://staging.montemagno.com/tag/xamarin/ https://staging.montemagno.com/content/images/2017/08/MonogramIcon_Orange_NoBuffer.png Xamarin - James Montemagno https://staging.montemagno.com/tag/xamarin/ Hugo -- gohugo.io Tue, 13 Oct 2026 20:48:55 GMT 60 Developing Android Apps on Windows Arm Devices https://staging.montemagno.com/developing-android-apps-on-windows-arm/ 644961ecfaa091003df357e4 Thu, 27 Apr 2023 23:00:11 GMT

I have gone all in on Arm devices! My main MAC machine is a Mac Book Air M1 and now my main driver at work is a Windows Dev Kit 2023 (aka Project Volterra). One tricky thing for developers is getting all the software we use to work properly on Arm. Things have come a long way since the original release of Arm devices for Mac and Windows. I have a full setup guide for my M1 for Xamarin and now .NET MAUI development, but what about Windows?

Visual Studio 2022 recently released a full native Arm version and they have been adding more workloads for developers to use. .NET MAUI was recently introduced, which got me excited but I right away ran into an issue, which was that there are no compatible Android emulators for Windows Arm and apparently no plans from Google :(. So, what are our options? Two come to mind.

Deploy to a device

Well, it may be obvious, but you can just plug in a device and use something like Vysor to mirror your device. I am always a fan of deploying and testing on devices as you have full access to everything including Google Play services. As a bonus you don't drag down any resources on your main machine. If you don't have a device, you are still in luck!

Windows Subsystem for Android

That's right! I have blogged and talked about it a bunch, but the Windows Subsystem for Android(WSA) is a great way to deploy and debug applications on any Windows 11 machine. It has expanded into more regions since its first release, which is awesome. If you haven't heard about WSA, it is a mode that you can enable that allows you to run Android applications directly on your Windows machine. There are limitations of course, the largest being no access to Google Play services, but for a lot of developers this will get you off the ground running. Checkout my full video:

If you are using Visual Studio 2022 there is a great extension that will help you get up and running and automatically connect.

Once you have it all set up, it is ready and available!

So, there you go! Two ways to get deploying Android apps if you are developing on a Windows Arm device.

]]>
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

]]>
Implementing In-App Subscriptions in iOS & Android with no backend servers https://staging.montemagno.com/ios-android-subscription-implemenation-strategies/ 62014fb35f3a88004810373c Mon, 07 Feb 2022 21:00:17 GMT

Over the years I have created several apps that I am super proud of, and for the most part, I have always put them out for free onto the app stores. More recently I have been experimenting with different monetization strategies for the apps so users can unlock features or just leave a tip. For the longest time I kept things simple with a "non-consumable" one-time purchase. The strategy is straight forward, they either purchased the item or they didn't. Based on this information you would unlock the features in the app. My nifty InAppBilling .NET plugin for iOS, Android, macOS, and Windows comes in super handy and makes it just a few lines of code to implement this logic. Over the holiday break I decided to take things a step further and dip my toes into the world of subscriptions, and what I found left my code in a complete state of madness! Before I go into details on how I decided to implement subscriptions, I recommend listening to our recent Merge Conflict episode all about subscriptions!

One of my goals of this was to not use any backend servers or accounts at all as I didn't want to add additional costs to management. That leads to even more complexity, but for me it was worth it, so let's get into it!

Setting up subscriptions

The first thing that you are going to need to do is setup your subscriptions in the App Store and Google Play. Each store has the concept of "Groups" that have multiple subscription options in them that users can upgrade/downgrade from. I am just offering a single subscription to keep things simple. The settings are straight forward:

  • How long is a subscription (1 week, 1 month, 2 months, 3 months, 6 months, 1 year)
  • How much is the subscription (as low as 0.49)
  • Name, Description, and Product ID

It is important you select the correct subscription length as you can't change this after it is approved. Additionally, you should set a unique Product ID across your apps and try to name them the same between iOS and Android for simplicity later. You can change the price later if you desire, but note that lowering it will affect all users, but if you increase you have the option to make it for all users or just for new subscriptions. I prefer to make it a bit higher to start and then adjust later as needed.

Apple and Google have great documentation if you need more information.

Purchasing subscriptions

Now that we have setup the subscription information, we can start our implementation. I mean technically you could skip the step ahead as well if you haven't prepped the app store yet, but of course you won't be able to test out things.

Purchase screen for subscription
Purchase screen for subscription

Purchasing is straight forward, and for this blog I am going to use my InAppBilling plugin that under the hood uses StoreKit and the Android Billing Library. Making the purchase is the same as any other purchase using the SDK, but the information that is returned is the most important part.

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

    IsBusy = true;
    try
    {

        // check internet first with Essentials
        if (Connectivity.NetworkAccess != NetworkAccess.Internet)
            return;

        // connect to the app store api
        var connected = await CrossInAppBilling.Current.ConnectAsync();
        if (!connected)
            return;

        var productIdSub = "mysubscriptionid";

        //try to make purchase, this will return a purchase, empty, or throw an exception
        var purchase = await CrossInAppBilling.Current.PurchaseAsync(productIdSub, ItemType.Subscription);

        if (purchase == null)
        {
            //nothing was purchased
            return;
        }

        if (purchase.State == PurchaseState.Purchased)
        {
            Settings.SubExpirationDate = DateTime.UtcNow.AddSubTime();
            Settings.HasPurchasedSub = true;
            Settings.CheckSubStatus = true;

            // Update UI if necessary if they have
            SetPro();

            try
            {
                // It is required to acknowledge the purchase, else it will be refunded
                if (DeviceInfo.Platform == DevicePlatform.Android)
                    await CrossInAppBilling.Current.AcknowledgePurchaseAsync(purchase.PurchaseToken);
            }
            catch (Exception ex)
            {
                Logger.AppendLine("Unable to acknowledge purcahse: " + ex);
            }
        }
        else
        {
            throw new InAppBillingPurchaseException(PurchaseError.GeneralError);
        }
    }
    catch (InAppBillingPurchaseException purchaseEx)
    {
        // Handle all the different error codes that can occure and do a pop up
    }
    catch (Exception ex)
    {
        // Handle a generic exception as something really went wrong
    }
    finally
    {
        await CrossInAppBilling.Current.DisconnectAsync();
        IsBusy = false;

    }
}

This looks like a lot of code, but it is only four calls to the billing service itself: Connect, buy, acknowledge (on Android), disconnect. And funny enough, on iOS you don't even need to really connect or disconnect as it doesn't do anything.

Now, the real thing to pay attention to is what we do when the user makes a successful purchase. I store 3 values that I store in preferences using Essentials:

   Settings.SubExpirationDate = DateTime.UtcNow.AddSubTime();
   Settings.HasPurchasedSub = true;
   Settings.CheckSubStatus = true;
  • SubExpirationDate: This value lets me check if the subscription is still valid and when to refresh the subscription status next.
  • HasPurchasedSub: A simple value to determine if they have EVER purchased a subscription. Just valuable information that I will use later.
  • CheckSubStatus: This value I use to determine if I have prompted the user to refresh the subscription status, it is true if the subscription is valid, and I have never prompted them.

That AddSubTime is a nice little extension method I made throughout my app to make it easier when checking if subscriptions are valid or not.

public static DateTime AddSubTime(this DateTime dateTime)
            => dateTime.AddMonths(1).AddDays(5);

I decided to add not only a month, but also five extra days just in case there is some weird billing issue. I figure what an extra five days anyway.

Now throughout my app I can run a single check to see if they have purchased a subscription and if it is valid:

public static bool IsSubValid => HasPurchasedSub && SubExpirationDate > DateTime.UtcNow;

Handling subscription renewals and cancelations

At this point, the user now has access to the subscription for a full month (and 5 days)! What happens when it is time to refresh that subscription status? I went with a simple approach on launch of the application that notifies the user that the subscription status needs to be refreshed. I could do this in the background, but I made it very transparent to the users.

if(Settings.CheckSubStatus && Settings.HasPurchasedSub && !Settings.IsSubValid)
{
    Settings.CheckSubStatus = false;
    if(await DisplayAlert("Subcription Status Refresh", "Looks like it is time to update your subscription status. If you canceled and resumed, you can always refresh your status on the settings page by restoring purchases.", "Refresh status", "Maybe Later"))
    {
        if (Connectivity.NetworkAccess != NetworkAccess.Internet)
        {
            Settings.CheckSubStatus = true;
            await DisplayAlert("No internet", "Please check internet connection and try restore purchases again in settings.", "OK");
            return;
        }
        //refresh status here
        await RestorePurchases();
    }
}

Here, I check to see if they ever purchased a subscription, if it is no longer valid, and if I should check the subcription status with a prompt. If they want to refresh the status, I call the RestorePurchase method.

Now, this is where things get FUNKY and weird because iOS and Android differ so much.

  • iOS: returns every single transaction including the original purchase and all renewals with proper transaction date time stamps!
  • Android: Only returns current valid subscriptions and the time stamp is always the original purchase date of the subscription, not the renewals.

I personally prefer the iOS mechanism for this as it makes the code much cleaner. All you must  do is order the purchases descending by transaction date and compare dates to see if a subscription is valid. On Android, if it returns a subscription then it is valid and you must figure out the expiration date manually by adding time over and over to the original transaction date, which seems problematic. However, after much testing, here is what I came up with to check to see if the subscription has been renewed:

async Task RestorePurchases()
{
    if (IsBusy)
        return;
    IsBusy = true;

    try
    {

        var connected = await CrossInAppBilling.Current.ConnectAsync();
        if (!connected)
            return;

        var foundStuff = false;

        var subs = await CrossInAppBilling.Current.GetPurchasesAsync(ItemType.Subscription);

        if (subs?.Any(p => p.ProductId == productIdSub) ?? false)
        {
            var sorted = subs.Where(p => p.ProductId == productIdSub).OrderByDescending(i => i.TransactionDateUtc).ToList();
            var recentSub = sorted[0];
            if (recentSub != null)
            {
                // On Android as long as you have one here then it is valid subscription
                if (DeviceInfo.Platform == DevicePlatform.Android)
                {
                    foundStuff = true;
                    Settings.HasTippedSub = true;
                    Settings.CheckSubStatus = true;

                    //loop through transactions and keep adding a month until valid
                    var date = recentSub.TransactionDateUtc;
                    while (date < DateTime.UtcNow)
                        date = date.AddMonths(1);

                    Settings.SubExpirationDate = date.AddDays(5);

                    SetPro();
                }
                else
                {
                    if (recentSub.TransactionDateUtc.AddSubTime() > DateTime.UtcNow)
                    {
                        foundStuff = true;
                        Settings.HasTippedSub = true;
                        Settings.CheckSubStatus = true;
                        Settings.SubExpirationDate = recentSub.TransactionDateUtc.AddSubTime();
                        SetPro();
                    }
                }

                if (DeviceInfo.Platform == DevicePlatform.Android && !recentSub.IsAcknowledged)
                {
                    try
                    {
                        await CrossInAppBilling.Current.AcknowledgePurchaseAsync(recentSub.PurchaseToken);
                    }
                    catch (Exception ex)
                    {
                        Logger.AppendLine("Unable to acknowledge purchase: " + ex);
                    }
                }
            }
        }

        if (!foundStuff)
        {
            await DisplayAlert("Hmmmm!", $"Looks like we couldn't find any subscription renewals, check your purchases and restore them in settings. Don't worry, all of your ride data will be saved.", "OK");
        }
        else
        {
            await DisplayAlert("Status Refreshed!", $"Thanks for being awesome and subscribing for another month of My Cadence Pro!", "OK");
        }

    }
    catch (Exception ex)
    {
        Debug.WriteLine("Issue connecting: " + ex);
        await DisplayAlert("Uh Oh!", $"Looks like something has gone wrong, please check connection and restore in the settings.  Code: {ex.Message}", "OK");
    }
    finally
    {
        IsBusy = false;
        SetPro();
        await CrossInAppBilling.Current.DisconnectAsync();
    }
}

It is not pretty, but totally working well. Note that for iOS I take the most recent subscription, add time, and compare it with the current UTC time: recentSub.TransactionDateUtc.AddSubTime() > DateTime.UtcNow and if it is still valid then I update the expiration date.

Verifying & restoring a subscription

One important feature that your app must implement is a "Restore Purchases" feature for in-app purchases and subscriptions. This is important if your user gets a new device, has multiple devices, or uninstalls and re-installs the app. I add a button directly on the settings/upgrade screen to do this. The application logic is actually the same as the renewal check above! Code re-use to the max!!! You may have other in-app purchases that you may want to put in there as well, so be sure to restore all your purchases!

Receipt validation

At this point we have finished all the in-app code for subscriptions, renewals, and restoration. See, it wasn't that bad :).... well except that we haven't done any receipt validation, which means the user could have cancelled, asked for a refund, paused a subscription, or who knows what else. Oh, and receipt validation also ensures that users haven't hacked your app in some way to intercept the calls to get free stuff. Look how easy this looks:

Receipt validation is super important, and I recommend you checkout my documentation with tons of links to other important resources including how to use Azure Functions as a receipt validation service. For this blog, and in my apps currently I am not doing receipt validation. I haven't personally had any issues, but my apps are only downloaded a few thousand times, not millions of times. So, I am going to leave this up to you based on your application.

Testing subscriptions

Now is the fun part of testing!!!!!!!

This is the worst part because when you test everything is different in a sandbox, and each platform is different. I ended up using TestFlight and Google Play Internal rings for testing with my normal account. When you subscribe on iOS and Android you aren't charged anything during testing, but they do renew differently for testing purposes. Android will just continue to renew every 5 minutes (for a 1 month sub, longer for others) and then you have to manually cancel the subscription. I like this approach a lot as it doesn't clutter up all my transactions and lets me do more realistic world testing. iOS is a bit of an interesting one as it also will renew the subscription every 5 minutes but will do it up to 12 times and then stop renewing. This is a fine approach, but I just wish they worked the same.

I could write for days on testing and all the complexity, but there is an amazing blog for iOS testing from RevenueCat that saved my life and outlines everything you need to know. Google actually has pretty good documenation for this and really helped me get through the process. Take your time on testing and make sure to test all of the different scenarios. One pro tip that I can offer is to change the amount of time for a valid subscription to 5 minutes (instead of my 1 month & 5 days) so you can test out the duration that Apple/Google renew on. Once you have validated that you can change it back and ship to the store.

In-App Subscriptions SaaS... RevenueCat

Implementing in-app subscriptions can be extremely complicated and can get even more complicated when you throw in user accounts, third party integrations, and subscriptions on the web. After reading the testing blog on RevenueCat, I realized that it was an entire SaaS solution for in-app subscriptions and is genius.

If you are really looking for a full solution this may be a good option for you to investigate. I have still decided to roll my own and use the raw APIs of the platform, but I have to say RevenueCat is pretty awesome and just shows how complex these APIs and testing matrixes are.

Other Considerations

In-app subscriptions are pretty neat as they offer a lot of flexibility over a single IAP or up-front app purchase. For example you can:

  • Offer a free trial for a month
  • Offer discounted rate for the first X months
  • Offer discounts for longer subscriptions
  • Promote them in the app store as another entry (iOS)

It is true that we are becoming a subscription based society and some people hate subscriptions. However, if you have great features and want to help your long-term development it is a great option to generate revenue. One option that I have in my apps is the option to do a "life time" subscription purchase. This is actually not a subscription, but a non-consumable in-app purchase. This adds a bit of complexity to the mix, but I think it is worth it at the end of the day. I hope that you found this blog a bit interesting and helpful on your IAP journey!

Checkout my videos on YouTube

]]>
Goodbye Android Emulators, the Windows Subsystem for Android is Here! https://staging.montemagno.com/goodbye-android-emulators-windows-subsytem-for-android-is-here/ 6172b75df2a01a003b1a2b76 Fri, 22 Oct 2021 17:39:00 GMT

Android Emulators have been a pain in everyone's side for pretty much ever. Intel HAXM is great, but limited to Intel, then there were custom emulators like Xamarin Android Player and Genymotion, but required additional installs, and finally Hyper-V support, which brought with it AMD support, and side by side Docker support as well. But now, Windows 11 is here and so is a brand new Insiders Preview that brings with it Android app support, which is CRAZY AWESOME. More so is that you can use the new Windows Subsystem for Android to develop, debug, and deploy your Android apps from your favorite IDE! Let's walk through what you need to do to get it all setup. However, if you want to just watch a video I have that for you as well!

Windows Subsystem for Android

So, what is this thing? Well, it is like the Windows Subsystem for Linux enabling you to have a full subsystem available for Android instead of Linux. Now at this point, you don't have access to 100% Android. It is not a device just sitting there, but instead think of Windows being the OS that can run Android apps. There is no Android launcher because Windows is the launcher of the apps.

Get Windows Insiders Beta

The first thing that you will need is the Windows Insiders Beta build (not Dev) that matches Windows 11 Build 22000.xxx. You can read all about how to turn on Windows Insiders on their blog.

Windows 11 21H21
Windows 11 21H21

Install Windows Subsystem for Android

Now it is time to install the Windows Subsystem for Android, which also comes with the Amazon App Store. This is done directly from the Microsoft Store on Windows and will require a reboot and some final installation steps.

App Store Listing for WSA
App Store Listing for WSA

Once it is installed you will want to search and pin the Windows Subsystem for Android app.

Search for Android
Search for Android

Developer Mode Setup

Inside of the WSA app you will find all sorts of great options, but a few things to turn on are "Continuous" mode, so the subsystem is always ready to go.

Then, flip on Developer mode:

Notice here that once you turn on developer mode you get instructions on how to connect to adb.

ADB Connect

I am going to steal this part directly from the documentation. To connect to the Windows Subsystem for Android VM for debugging, you have two options:

Recommended Method:

  • Use localhost for connecting to debugging. The IP address of the localhost is: 127.0.0.1:58526. Windows Subsystem for Android must be running in order to connect, the best way to launch Windows Subsystem for Android is by launching an Android app that was installed with the Amazon Appstore.
  • To connect to the localhost address of Windows Subsystem for Android, enter: adb connect 127.0.0.1:58526

Alternative Method: Use the Windows Subsystem for Android Settings app to get the IP address.

  • Launch the Settings app. (Use Windows Search to select and launch.)
  • The IP address will be displayed under the IP address section. If there is no IP address being displayed, launch an Android app that was installed using the Amazon Appstore, then select Refresh on the IP address button in the Settings app.

Now that you have the IP address to connect to the Windows Subsystem for Android VM, connect using adb connect: adb connect 172.22.137.166

Originally, I had to connect through the IP address, however after a reboot and relaunching WSA, I got a firewall prompt and then 127.0.0.1 started working for me.

Debug!

Now that you are connected via ADB, WSA will just show up in your IDE of choice because it is a device!

Debug away!

Your Apps are on Windows

That is right, your apps are on Windows! You don't need to boot things up to see your app because they are literally in the start menu!

Also, when you open the app setting, they are the Android app settings! How cool is that!?!?!

Limitations

There are a few things to be aware of with WSA. There is no Google Play Services, so if you are testing out maps or other services that is not going to work. It isn't an emulator, which means you don't get to control the skin or conditions under where your app runs since it is running under WSA, not an emulator. Not every sensor is connected, for example it looks like Bluetooth isn't forwarded to WSA from your Windows machine. I also haven't found a way for apps running under WSA to talk about to the local machine for debugging web servers, but I am sure it is a matter of time before someone figures it out.

However, this is the very first preview of WSA and I am extremely happy with it! I love it and never want to open an Android emulator ever again!

Checkout my videos on YouTube

]]>
Staged Rollouts & Phased Release on Google Play and App Store Connect https://staging.montemagno.com/using-phased-releases-on-google-play-and-app-store-connect/ 61362c9aba5764003b25c185 Mon, 06 Sep 2021 16:05:50 GMT

As an app developer it is hard enough to create your app, test your app, prep the app store listings, and then get your app approved. The last thing you want to think about is how fast or slow to roll out your app and updates to users. For the past 10 years all I have ever done was just release the app to everyone all at once, and for a long time this all that was available to us. However, now there are options for developers from both Google and Apple that can manage your release. Unfortunately, Google's staged rollouts and Apple's phased release aren't exactly the same, so let's talk about them.

Why a phased release?

Because we don't want a bunch of 1-star reviews if things go wrong 😉. I joke, but it is true that one of the reasons is that we don't know if something is going to go wrong once the app is released. If you are like me, I do rigorous testing in development and through Test Flight and Google Play's internal testing. However, I don't own every single device out there and I don't know the state of the current app on each user's device. Once an app update is out, there is no undo button, there is no taking it back. Therefore, planning a phased release or staged rollout is important. We can release the app to limited number of users each day and get real-time feedback from analytics, support emails, or even app store reviews. It is better to handle these problems in a limited scope before it gets out to all users. The downside is that you will need to plan your release and updates at least a week ahead of time, but eventually as your app grows it is going to be worth it.

Apple Phased Release

For iOS and Mac apps Apple has the ability with one radio button to flip on phased released when you submit your app for review. If you have it set to manual release you can also change it after the app has been approved. Apple phased releases are very Appley, which means it is well thought out, but you have very little control over exactly how it is rolled out. When you turn on phased release your application will be fully rolled out over 7 days to all your users at increments of 1%, 2%, 5%, 10%, 20%, 50%, and 100%. At any time, you can pause the phased release.

Phased release plan by Apple for iOS and Mac Apps
Phased release plan by Apple for iOS and Mac Apps

One percent is a small number for the first day especially if you don't have a large install base. I recently did this rollout for My Cadence, and it meant that around thirty-five users got the update on day one. Seeing that my app isn't used every single day and a user needs to do a bike ride, who knows how many people used the update. It really isn't until around day 4 that my sample size had increased to around 350 users and I could check my app analytics to see if upgraded users are using the app and if crashed are occurring.

At this point I would have liked to pause for a few days, monitor the app, and then continue with the release, but that is not an option. The phased release continues and increases the user base fast. I understand why Apple limits the rollout, but a tiny bit more control would be nice.

Now, what if you are a few days into a release and decide the update is good to go and you want to release it to all users. Don't worry, Apple has you covered with a big release to all users button. If you have a large user base, you may end up using this option by day 3 or 4 because you should have the data you need.

You always have the ability to release to all users
You always have the ability to release to all users

Google Staged Rollout

As I mentioned earlier the two companies have similar features but work completely different. Google's approach feels super Googley. They give you 100% control with extremely limited guidance and no restrictions at all. You enter a number into a box and away your staged rollout goes. You can start with any percentage you would like, and they give you an estimate of how many users will get this update. From there you can let it sit at any percentage for as long as you would like. You can update the rollout percentage 1% at a time or set it to anything you would like. Once you set it to 100% it is fully rolled out. This approach is genuinely nice because you can rollout the release over any amount of time, but it is a 100% manual process, which is not super fun. I ended up doing 20%, 40%, and then 100% over a week.

You select what % your rollout is on Google Play
You select what % your rollout is on Google Play

Each app store has its own advantages and draw backs when it comes to phased releases. If you take a bit of time to plan out your releases, you can save yourself some headaches if something goes wrong.

Listen to the podcast

Frank and I recently had this very topic on our podcast Merge Conflict. Take a listen.

Checkout my videos on YouTube

]]>
Long-running Operations on macOS and iOS with NSProcessInfo https://staging.montemagno.com/long-running-processes-ios-macos-nsprocessinfo/ 6117d5c0ba5764003b25bff5 Sat, 14 Aug 2021 15:04:35 GMT

One issue that sometimes comes up in app development is keeping your app alive and running during long running processes. In my case with My Stream Timer, when the user starts a timer, it needs to keep running no matter what. My first solution was to simply turn on Screen Saver mode.

var appDelegate = ((AppDelegate)NSApplication.SharedApplication.Delegate);

appDelegate.MainWindow.Level = NSWindowLevel.ScreenSaver;

This solution "works" but has the side effect that the app will always be on top all the time, no matter what.  As you could imagine this is not an ideal solution. Then out of nowhere Frank told me about starting and stopping Activities with NSProcessInfo. We then did a full podcast on it:

I of course ignored his advice until this morning when I wrote 40 lines of code that solved all of my problems.

What are Activities?

According to Apple:

"The system has heuristics to improve battery life, performance, and responsiveness of applications for the benefit of the user. You can use the following methods to manage activities that give hints to the system that your application has special requirements."

What this means when you Begin or Perform and Activity is that:

"the system disables some or all of the heuristics so your application can finish quickly while still providing responsive behavior if the user needs it."

This is pretty awesome! There are also two distinct categories of activities based on what you app is looking to do.

  • User-initiated activities are explicitly started by the user. Examples include exporting or downloading a user-specified file.
  • Background activities perform the normal operations of your application and aren’t explicitly started by the user. Examples include autosaving, indexing, and automatic downloading of files.

In my case I have a "User-initiated" activity where I want to keep the screen on, app alive, and the processor going. There are several different types of NSActivityOptions that you have fine grain control over to specify this:

In my case this is a NSActivityUserIntitated, which includes NSActivityIdleSystemSleepDisabled, but I also want NSActivityIdleDisplaySleepDisabled. I also have multiple activities running at the same time, so I created a small manager to handle this.

Start Activity

Dictionary<string, NSObject> Activities { get; } = new Dictionary<string, NSObject>();

public void StartActivity(string id)
{
    if (Activities.ContainsKey(id))
    return;

	try
	{
		var options = NSActivityOptions.UserInitiated | NSActivityOptions.IdleDisplaySleepDisabled;

		var activity = NSProcessInfo.ProcessInfo.BeginActivity(options, "User has inititiated a timer that is a long running process.");

	Activities.Add(id, activity);
	}
	catch (Exception ex)
	{
		Debug.WriteLine($"Unable to start activity: {ex}");
    }
}

Now, when the user starts a timer, I kick off an activity and I am good to go.

End Activity

Now, I need to stop it when the timer ends or the users stops or pauses the timer.

public void StopActivity(string id)
{
    if (!Activities.ContainsKey(id))
        return;
    try
    {
        var activity = Activities[id];
        NSProcessInfo.ProcessInfo.EndActivity(activity);
        Activities.Remove(id);
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"Unable to start activity: {ex}");
    }
}

That is it! Easy peasy, and now the system will keep My Stream Timer will continue running without having to stay on top.

]]>
Setting Up an M1 Mac for Xamarin Development https://staging.montemagno.com/setting-up-an-m1-mac-for-xamarin-development/ 6063cbb020df30003b2b3c61 Wed, 31 Mar 2021 23:01:32 GMT

Who just upgraded their MacBook after 8 years?!?! This guy!!! Let's set it up for mobile development with Xamarin for iOS & Android development in C#.

James with his M1 MacBook Air

That is right, I finally decided to go all in on an M1 MacBook Air (8-Core GPU, 512GB SSD, 16GB RAM) thanks to the $500 "credit" from participating in the DTK program.  Not only was it time for an upgrade since my machine was 8 years old, but also because this new generation of machines marks a pivotal transition for macOS devices moving to ARM64 with the M1 chip.

MacBook air in Gold sitting on my table
So Pretty!

Early Adopter

Before I made this purchase I had to acknowledge to my that I would be an early adopter of a brand new technology. I knew that everything wasn't going to be perfect or even run at all. Rosetta 2 is supposed to make all apps work just magically, but if you are a developer there are a plethora of tools in the tool-chain that are required to get things working. For Xamarin you need Xcode, iOS Simulators, Xcode CLI, Android SDK, Android Emulators, Visual Studio for Mac, Mono, Xamarin.iOS & Xamarin.Android SDK, and all the things that come with it. So yeah... lots of things that need to be updated.

The release of Visual Studio for Mac 8.9 and macOS 11.2.3 introduced optimizations that made things work better while still tracking any outstanding issues on Developer Community. That said I am happy to say that with a little bit of setup things mostly just work and are pretty snappy!

So, let's get started!

Visual Studio for Mac

This is the easy part of getting setup because the installer is pretty great. Just check those checkboxes for iOS, Android, and macOS development. The installer will also install .NET Core 3.1 and .NET 5. From my findings .NET 5 works best works for development of ASP.NET and .NET console apps. Alright, you are almost done :)

Xcode & CLI

For iOS apps you will need to install Xcode and the command line dependencies. Xcode 12 can be downloaded directly from the App Store and for me took about an hour to install so go make and eat a sandwich and go on a walk.

After installing Xcode make sure you open it, accept that beautiful EULA, and then attempt to find how to install the "Command Line Tools for Xcode". This usually installs automatically, however for me, it didn't so I signed into the Apple Developer download portal and installed it manually. If you fail to install this then Visual Studio for Mac won't be able to find any of your simulators. After that you should be good to go to properly start running and debugging iOS apps!

Visual Studio for mac running iOS app with Hot Reload!

Android Emulator for M1

Who would have thought that Android Emulators would be a headache!?!?!! Rosetta 2 is amazing, but when it comes to the default Android Emulator it falls over because it can not run Kernel extensions and doesn't support VMs that are doing x86_64 virtualization. Do not fear though because Google has actually been working on fixing this and has a special native ARM64 build of the Android Emulator! They have been building it out on GitHub and it is now included in the Android Studio AVD manager, however it isn't available just yet in the Visual Studio for Mac AVD manager :(.  A workaround for now is that you can just download and run it manually from their GitHub releases page! I downloaded Preview 3 and installed it into the Applications folder.

To launch the emulator you will need to hold down the control button and then double click launch it the first time.

When you launch it you will get a warning that adb could not be located and must manually be assigned.

Warning in the Android emulator about ADB

This location can be set manually in the ... of the Android Emulator under Settings. Turn off the "Use detected ADB location" and then browse for adb manually that Visual Studio for Mac uses.

Android emulator settings showing how to manually set adb location

You can find this in VSM -> Preferences -> SDK Locations -> Android:

Android SDK Location in VSM

The actual path of adb will be /Users/YOUR_USER_NAME/Library/Developer/Xamarin/android-sdk-macosx/platform-tools/adb. After you locate this, you will be good to go! It will show up now when you run the emulator (I pinned it) under the name :).

Android app running in Visual Studio with Hot Reload

Pretty Fast!

Now, most of the tools here are all going through Rosetta 2, but I have to say that overall it feel pretty fast. I picked up the MacBook Air and not the Pro and I can create a new project and get up and running on a simulator in under 1 minute, which is pretty great:

More to come!

As an early adopter I know things aren't going to be perfect, but things are looking pretty good after playing around for a few hours. I need some more time to setup my entire environment and tools to get a real feel for things. I haven't experimented with every workload yet, but most of the traditional ASP.NET Core, and Blazor Server things seem to work well from my testing so far. Blazor WebAssembly seems to have a few issues, but that feel like something that will get worked out soon. If you have an M1 and find any issues with Visual Studio for Mac please report a problem on the Developer Community.

Hopefully you found this pretty interesting if you are in the market for an M1 or have just been struggling to figure out this new landscape of ARM64 recompiled apps.

]]>
Cross-Platform In-App Purchases for Xamarin.Mac Apps! https://staging.montemagno.com/cross-platform-in-app-purchases-for-xamarin-mac-apps/ 6022c9a56c48dd0039592ea7 Tue, 09 Feb 2021 18:04:20 GMT

This week, one of my best friends Frank Krueger released his brilliant app iCircuit 3D for macOS (he also wrote an awesome blog on how it was made). While I know that catalyst support for Xamarin (and .NET MAUI) is coming soon, I still think there are great opportunities to monetize macOS apps written in C# today (see iCircuit 3D!). I released my In-App Billing Plugin a few years ago for iOS, Android, and UWP apps and it has been awesome to hear how it has helped so many developers monetize their apps. The library has expanded over the years to support subscriptions and updated to the latest Android billing libraries. I have used this library personally to monetize my apps including Island Tracker and My Cadence. If you haven't seen the API it is absolutely lovely! Check this out:

Get Product Info

Everything is basically three lines of code... Connect, Call, Disconnect:

var connected = await CrossInAppBilling.Current.ConnectAsync();
if(!connected)
    return;

var items = await CrossInAppBilling.Current.GetProductInfoAsync(ItemType.InAppPurchase, "myitem");

await CrossInAppBilling.Current.DisconnectAsync();

Purchase an Item

Rinse and repeat!

var connected = await CrossInAppBilling.Current.ConnectAsync();
if(!connected)
    return;

var purchase = await CrossInAppBilling.Current.PurchaseAsync(productId, ItemType.InAppPurchase);
if(purchase != null & purcxhase.State == PurchaseState.Purchased)
{
	//item bought
}

await CrossInAppBilling.Current.DisconnectAsync();

Now for Xamarin.Mac!

I recently had a few user of My Stream Timer as me how they can donate and support the development of the app. I never really thought about monetizing the app as I made it sort of for myself to show a countdown before my streams. However, this app has grown to nearly 20,000 downloads from all sorts of streamers including yoga instructors, teachers, coders, and gamers! So I figure I would extend support for my plugin to macOS. This weekend I sat down and 3 hours later it was done! Luckily for me the API is extremely similar to iOS with StoreKit. There are a few differences around making the actual purchase where you can't specify a string id, but instead must get the actual product from StoreKit first. Luckily this method already existed in my code and after a bit of hacking I got it working. Oh and there is no TransactionReceipt, which is weird but there are newer APIs that developers are supposed to query. Beyond that the API works as expected! Check it out!

In app purchase in My Stream Timer

My favorite part has to be testing IAPs on macOS because it is so simple. All you need to do is log out of the app store and then open your app in debug and make the purchase! It just works! I love it!

If you are making macOS apps I hope that you will checkout the plugin and try to add some monetization to your app!

]]>
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

]]>
Porting My Cadence to Android in 1 Day! https://staging.montemagno.com/my-cadence-now-available-on-android/ 5fe7ca72875b770039372338 Sun, 27 Dec 2020 18:31:00 GMT

It seems like it was just yesterday that I was writing up a blog post on how I was launching My Cadence for iOS and then blogging about how I wrote and shipped the app in under a week. Well, it wasn't yesterday, but it was just 5 days ago! After I shipped the iOS version, I was watching some HGTV on Hulu and thought to myself, "how long would it take me to ship this app on Android?". The answer is around 4 hours' worth of work, and about 4 days of waiting for Google Play to approve my application for testing and publishing. Just like that it is live on Google Play for your Android device!

Enabling Android Support

I can't think of a real reason I decided to enable Android support for My Cadence. I guess maybe I was just board on my first day of holiday break and wanted a quick challenge. It was as if as soon as the challenge started, I was already done thanks to Xamarin and all of the amazing cross-platform libraries I had used.

When I started the iOS app, I kept things quite simple all in a single project and everything in the code behind. So, the first thing I needed to do was to break thing out into a .NET Standard library and ensure I didn't have any iOS specific code in the code behind.

Solution for My Cadence with Android and iOS projects

This took all of 15 minutes or so and I was ready for the next task of seeing if everything "just worked".

Bluetooth Differences

Let's just say everything was close to working out of the box. The biggest difference is that on Android using and managing Bluetooth require the user to give location permissions. This is the oddest thing ever, however unless you use some newer APIs this is how it has to be done even though location isn't used at all. I created a small interface and used Xamarin.Essentials to access Permissions on Android.

public async Task<PermissionStatus> CheckAndRequestLocationPermission()
{
    var status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();

    if (status == PermissionStatus.Granted)
        return status;

    await MyApp.Current.MainPage.DisplayAlert("Permission required", "Location permission is required for bluetooth scanning. We do not store or use your location at all.", "OK");

    status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();

    return status;
}

The reason I put this into an interface and used the dependency services is because I don't need to request these permissions on iOS. If I had left the code in the .NET Standard library than I would have needed to add the rationale into the info.plist. The linker is smart, but not that smart.

In-App Purchases

I love and hate in-app purchases. Mostly because they are a pain to implement, test, and keep up to date. Luckily, I just went through a new re-write of my plugin for billing that handles everything for you.

The only bit of code that I needed to add was the new "AcknowledgePurchase" method after the user has purchased the item.

 if(DeviceInfo.Platform == DevicePlatform.Android)
    await CrossInAppBilling.Current.AcknowledgePurchaseAsync(purchase.PurchaseToken);

The next hardest part was trying to test it. You MUST put it in an open or closed beta/alpha release to test this in Google Play. These must be "reviewed" and can take several days to do. The internal track doesn't work at all for this and only works once you publish your app. It has been a while since I published a new Android app, and I was shocked at how long this took. However, after it is all said and done everything worked!

Theming Differences

The biggest difference between iOS and Android has to do with the theme colors. I support light and dark modes in the app and on iOS I keep it all simple styling. However, on Android the ActionBar on top really likes to be colored and the status bar background needs the background set. I pulled in a little code to style the status bar:

public void SetStatusBarColor(System.Drawing.Color color, bool darkStatusBarTint)
{
    if (Build.VERSION.SdkInt < Android.OS.BuildVersionCodes.Lollipop)
        return;

    var activity = Platform.CurrentActivity;
    var window = activity.Window;
    window.AddFlags(Android.Views.WindowManagerFlags.DrawsSystemBarBackgrounds);
    window.ClearFlags(Android.Views.WindowManagerFlags.TranslucentStatus);
    window.SetStatusBarColor(color.ToPlatformColor());

    if (Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.M)
    {
        var flag = (Android.Views.StatusBarVisibility)Android.Views.SystemUiFlags.LightStatusBar;
        window.DecorView.SystemUiVisibility = darkStatusBarTint ? flag : 0;
    }
}

Beyond that I just had to change a few settings for background colors and sizes of buttons and progress bars and then the app was ready to go. Here is a side by side of the app on Android and iOS.

It is amazing just how quick I could bring over this app. If you want to find out more information or download the app checkout mycadence.app.

]]>
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.

]]>
5 Must Install NuGet Packages for New Xamarin Projects https://staging.montemagno.com/essentials-nugets-for-new-xamarin-projects/ 5fc80a2879e5440039355a25 Wed, 09 Dec 2020 16:52:18 GMT

Starting your first project can be a bit overwhelming. I mean heck, I wrote nearly 3,000 words breaking down every bit of what to select and what each option means. Oh, and I also have a full video series walking you through it step by step on my YouTube channel. Before I start writing code in my projects I take some time to configure my projects and then install essential NuGet packages that help me be super productive from the start.

Yes, it is true that Xamarin.Forms and Xamarin.Essentials are included "in the box" with your new project, but there is a world of amazing libraries out there from amazing community members. I want to introduce you to my favorite packages that are key in ever app that I have developed. So, let's get started.

MVVM Helpers 🎉

I may be a bit bias with these first 2 NuGet packages because I wrote them, but I think they are super essentials. First is MVVM Helpers (Refractored.MvvmHelpers), a super small library that works in any app that contains nifty classes and utilities that help creating your app from the start. It includes things such as:

  • ObservableObject: A Simple implementation of INotifyPropertyChagned
  • BaseViewModel: The only base view model you will ever need. It implements INotifyPropertyChanged and a bunch of default properties such as Title, SubTitle, Icon, IsBusy, IsNotBusy, CanLoadMore.
  • ObservableRangeCollection: Best way to group items into a Key/Value pair ObservableCollection for managing groups of data.
  • AsyncCommand: An async version of ICommand!

So, now you can just start your project and write code like this:

public class MonkeyViewModel : BaseViewModel
{
	public MonkeyViewModel()
    {
    	Title = "Monkeys";
    }

    public ObservableRangeCollection<Monkey> Monkeys { get; } = new ();

    AsyncCommand doStuffCommand;
    public AsyncCommand DoStuffCommand => doStuffCommand ??= new AsyncCommand(AsyncMethod);

    string name;
    public string Name
    {
    	get => name;
        set => SetProperty(ref name, value);
    }

    public Task AsyncMethod()
    {
    	return Task.CompletedTask
    }
}

Monkey Cache 🐵

This nice little caching library enables you to easily store data objects for set amount of times. For example you may want to cache web results for a few minutes. It integrates directly with the file system, SQLite, or LiteDB so you can write code like this:

public async Task<T> GetAsync<T>(string url, int minutes = 3)
{
    var json = string.Empty;

    if (!Barrel.Current.IsExpired(url))
    {
    	json = Barrel.Current.Get<string>(url);
    }
    else
    {
        json = await client.GetStringAsync(url);
        Barrel.Current.Add(url, json, TimeSpan.FromMinutes(minutes));
    }

    return JsonConvert.DeserializeObject<T>(json);
}

Wow, data caching in 3 lines of code! :)

PancakeView 🥞

My apps can not live without the PancakeView NuGet from Steven Thewissen. It gives your apps a breadth of new life with beautiful gradients, cards, custom rounded corners, and so much more. I mean look at this beautiful thing:

Sharpnado 🦈🌪

Alright, technically Sharpnado is a bunch of smaller libraries, but you get to pick and choose because there is so much awesome from Jean-Marie Alfonsi.

First is Sharpnado.Tabs... fully customizable Tabs that you can put anywhere!

Then there is Sharpnado.Shadows, because you know you need to add lovely shadows to all of your views.

That is just the start as there is a plethora more available including the TaskLoaderView, HorizontalListView, and MaterialFrame. You need these libraries!

Xamarin Community Toolkit 🧰

I am really jazzed about this initiative because it is an official package from the Xamarin team, but a deep collaboration with the community. This toolkit is a collection of controls, Animations, Behaviors, Converters, and Effects for mobile development with Xamarin.Forms. It simplifies and demonstrates common developer tasks building iOS, Android, and UWP apps with Xamarin.Forms. The NuGet is still in pre-release phase for testing, but is packed full of amazing functionality including:

  • Toast
  • Snackbar
  • AvatarView
  • MVVM Helpers (awesome stuff from my library)
  • C# Markup Extensions
  • Tons more!

Even More NuGets

These are just a few of my recommended NuGet packages that you should totally check out. There are a lot more awesome community libraries out there that you should checkout and I have a curated list on my GitHub!

]]>
Build Your First for iOS & Android App with Xamarin and Visual Studio New for Xamarin. If you are a more visual learner, I have a full video that you can watch: Let's set some groundwork first by answering some frequent questions. ]]> https://staging.montemagno.com/build-your-first-for-ios-android-app-with-xamarin-and-visual-studio/ 5fc52f3b79e5440039355a08 Mon, 30 Nov 2020 18:18:12 GMT

Let's start our journey together to build beautiful native cross-platform apps with .NET, Xamarin, and Visual Studio! In this blog I am going to guide you through the entire process and describe everything that is in File -> New for Xamarin. If you are a more visual learner, I have a full video that you can watch:

Let's set some groundwork first by answering some frequent questions.

What is .NET? .NET is a cross-platform, high-performant, and open-source runtime that is used with multiple languages, editors, and libraries that enables apps to be built for web, mobile, desktop, games, and more. It is the foundation that enables code to be run on different platforms such as iOS, Android, and Windows. If you are a web developer this is like the V8 runtime that executes JavaScript or if you are a Java developer, the JVM that executes Java.

What is Xamarin? .NET has a series of frameworks that are built for it that enable unique scenarios for developer for specific platforms. For example, ASP.NET Core and Blazor are frameworks built for .NET for web development. WPF and UWP are frameworks built for .NET for desktop development. Xamarin is a framework and tooling that enables apps to be built for iOS, Android, and macOS. It also contains several libraries that enable cross-platform development across iOS, Android, macOS, and Windows including Xamarin.Forms for cross-platform user interface and Xamarin.Essentials for accessing native platform features cross-platform.

What programming languages can I use? .NET supports several languages including C#, F#, and Visual Basic. You can use any of these programming languages to build apps with Xamarin, however I am a C# developer, and it is the primary language for app development. However, there are some fantastic frameworks for F# such as Fabulous for functional development.

XAML is another "language" that will be used to build user interface with Xamarin. XAML has been used for a long time to build Windows applications with WPF and UWP and has some great features including easy to read markup, Hot Reload technology, and advanced data binding to make UI development a breeze.

What tools can I use? .NET enables developers to use a wide range of tools including command line, code editors such as Visual Studio Code, and full IDEs (Integrated Development Environment) with Visual Studio 2019 and Visual Studio 2019 for Mac. To develop mobile apps with Xamarin we will need to use either Visual Studio 2019 or Visual Studio 2019 for Mac as they have the full features to build apps.

So, let's get started building our first mobile apps with Xamarin!

Installing Visual Studio for Mobile Development

The first step in your cross-platform journey is to install Visual Studio 2019 for Windows or Visual Studio 2019 for Mac.

When installing Visual Studio 2019 on Windows all you need to do is select the Mobile development with .NET workload: Visaul Studio 2019 Installer with Xamarin checked

On Visual Studio 2019 for Mac you can select to install Android and iOS. Visual Studio 2019 for Mac Installer with Android and iOS checked

If you are doing iOS development you will also need to install Xcode on your macOS machine.

After this you are all set and ready for mobile development!

When you create and run an Android project you may get asked to install additional Android SDKs and create Android emulators.

Creating your project

Now that we have Visual Studio 2019 and Xamarin installed let's get to work! When you launch Visual Studio 2019 you will have an option to create a new project. On Windows you can search for Xamarin.Forms or use the project types filter and select Mobile. We will want to select Mobile App (Xamarin.Forms).

New project dialog with Mobile App selected

When you select this new project, you will see a new dialog asking what type of template for the app that you would like to use:

new project template selection for flyout, tabbed, and blank

We have a few options to select based on the type of application that we want to build.

Flyout, Tabbed, & Blank

Flyout & Tabbed app templates are based on the latest Xamarin.Forms technology called Shell. It enables quicker scaffolding of apps and offers unique features such as URL based navigation. These templates are similar with a main difference if you want a flyout menu or bottom tabs for the base of your app.

Blank app is just that and offers a quite simple template with a single page and nothing else.

Let's go with Flyout! I want us to start here as most tutorials don't go through Shell and to me it is the future of Xamarin app development. This template has a lot in is so in future posts we will go through each section, but for today we will just create the project and go through what a Shell is.

If you are on Visual Studio 2019 for Mac you will want to select Multiplatform > App > Flyout Forms App

What's in the project?

Now that the app project has been created you will see several projects inside of a single solution.

Solution with 3 projects including shared code, iOS, and android

Let's walk through what is here in our new solution:

The first project contains several folders, code files, and user interface files. This project is a .NET Standard project that enables us to have a single project that can be shared across different operating systems. This project is where we will write most of our code.

iOS & Android projects are the "head" or "parent" project that is used to house platform specific code, settings, assets, and more. This is where you will want to configure different settings such as display name, app icon, version numbers, and any code that is needed for iOS or Android specific things that aren't available cross-platform. At some point you will need to go into these project and tweak things around, but for now we will stay in the cross-platform project.

Xamarin.Forms Cross-Platform UI

Inside of our cross-platform .NET Standard project we will find a bit of code for our app. This template shows off a sample app that has a list of items and an about page. Items are organized into folders:

  • Models: These are classes that represent data in your apps such as Person, Car, Dog, Cat, etc. In this app we have a single model called Item.
  • Services: Inside of this folder is where you would put code that access non-UI related things such as web service calls, databases, etc. In the project in a simple MockDataStore that is used to store items in memory.
  • Views: This is where our XAML user interface files will live!
  • ViewModels: In this folder you will find files that map nearly 1:1 to the Views folder. This is because each View has a ViewModel that is bound that Xamarin.Forms can bridge the two together so the View can display information or call methods in each ViewModel.

Let's look at the AboutPage.xaml which is the first page we will see when we launch the app. Here we see a few things, first is the top-level element ContentPage:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MyAwesomeApp.Views.AboutPage"
             xmlns:vm="clr-namespace:MyAwesomeApp.ViewModels"
             Title="{Binding Title}">

Here are a few xmlns tags that represent where specific code lives. The default and x are included in every file and are for base functionality. The xmlns:vm tag allows us to access our own code from XAML, which we will see in a bit. The x:Class is a reference to the namespace and name of the page, this matches the code behind. Finally, there is a Title property that is data bound to a property called Title in our ViewModel.

We can set additional properties such as the BindingContext and Resources with XAML content. Here we are going to use that xmlns:vm tag to set the BindingContext to a new instance of the AboutViewModel:

<ContentPage.BindingContext>
    <vm:AboutViewModel />
</ContentPage.BindingContext>

Next up is a Color resource that can be used on the page. Resources allow us to remove duplicated code and create re-usable key/value pairs. This is similar in a way to CSS in web development.

<ContentPage.Resources>
    <ResourceDictionary>
        <Color x:Key="Accent">#96d1ff</Color>
    </ResourceDictionary>
</ContentPage.Resources>

We can add more things to the Resources including strings, styles, and more.

Finally, each Page can have one root element control. This is normally a Layout such as StackLayout, ScrollView, or a Grid like we have here. The grid is an awesome control that has rows and columns. We won't go much into detail of layouts, but this one displays an image, some text, and a button that can be clicked to open a web browser.

<Button Margin="0,10,0,0"
        Text="Learn more"
        Command="{Binding OpenWebCommand}"
        BackgroundColor="{StaticResource Primary}"
        TextColor="White" />

Note the {StaticResource Primary}, which is a reference to a resource we defined Accent earlier, but Primary is located in our App.xaml, which are resources avaialble across the entire app.

To find more layouts and controls you can open the Toolbox to see what is available:

Xamarin.Forms toolbox in Visual Studio

Finally, if you select a control or layout the Property pane will show you all the different properties that are available on that control.

Property Pane in Visual Studio showing properties of a button

One thing that is very important is that each of these XAML pages has code behind that is important for the XAML view and has specific methods for the constructor and event such as when the pages is shown or disappears. You can use the little arrow drop down to see the .xaml.cs code behind file.

AboutPage with drop down highlighted in project pane

If you open it up, you will see that it is very simple as for this view there isn't any special events that are needed right now.

public partial class AboutPage : ContentPage
{
    public AboutPage()
    {
        InitializeComponent();
    }
}

User Interaction and ViewModels

Now, let's talk about the ViewModel for this page that was created in the XAML. Open up the ViewModels -> AboutViewModel.cs file by double clicking on it.

public class AboutViewModel : BaseViewModel
{
    public AboutViewModel()
    {
        Title = "About";
        OpenWebCommand = new Command(async () => await Browser.OpenAsync("https://aka.ms/xamarin-quickstart"));
    }

    public ICommand OpenWebCommand { get; }
}

This is an amazingly simple ViewModel, but is a notable example of how data binding works. Here the ViewModel sets the Title to About and then creates a new Command that is calling Browser.OpenAsync to open a web browser on the page.

That Command is something special. Unlike Title which is a simple string that will be displayed, the Command is a special class that we can use to tell our user interface to do something when the user interacts with it. For example on our Button we saw the following code:

Command="{Binding OpenWebCommand}"

This code is telling Xamarin.Forms that there is a public Command in our ViewModel that has code to execute when the user clicks on the button. In this case we want to open the brower, which is one of the many native features available from Xamarin.Essentials. Calling Browser.OpenAsync will launch the browser specific for iOS and Android with a single line of code.

Quick Changes with XAML Hot Reload

Now, it is time to launch our application. I like to start on Android because emulators are available locally on both Windows and macOS. In the menu of Visual Studio you will see that Android is the default project and will the debug button is available and will show your Android Emulators available. When you click the debug button if you don't have any emulators you will be asked to created one.

Android Debug menu in Visaul Studio

When the app launches it will enter Debug mode which allows us to set break points on our code to see the values, but also enables XAML Hot Reload where we can make changes to the UI without having to stop debugging.

App launched with hot reload enabled

Let's open the AboutPage.xaml and change the Accent color to a different color. Here I will update it to Orange and I will hit save to see thechanges.

Android and XAML Hot Reload changing a background color

At this point we can add new controls, update the user interface, and continue to work on our app on Android.

Deploy to iOS with Hot Restart

Let's get iOS deployed! Right click on the iOS project and select Set as Startup Project:

Setting iOS as the startup project in the project properties

On iOS things are a bit different for developers. If you are on macOS and install Xcode you will have iOS Simulators available. If this is you, you can select a simulator and then debug your app. On Windows, there are no iOS simulators :( so there are two ways to get your

  • iOS Remoted Simulator: If you have a macOS machine you can install Visual Studio 2019 for Mac and Xcode and connect to it from your Windows machine to use it as a remote build machine. The iOS simulator can then be launched from displayed on your Windows machine, which is very cool!
  • iOS Hot Restart: This is a new preview technology that enables you to deploy a debug build of your app directly to your iOS device that is plugged into your Windows machine. This is great if you don't have a mac and want to debug your application and use awesome features like XAML Hot Reload.

Based on your setup and equipment available there is a great option for you to start debugging your iOS app from Windows. Read through the documentation that I linked to for a full setup guide.

Understanding App and Shell

The last thing I want to talk about is what is in our *App and what is in our Shell:

Alt Text

Inside of our App.xaml you will find global resources such as Primary that we used earlier on the button, but also styles such as a global Button style that we are using with states when enabled and disabled. If you open up the App.xaml.cs code behind you will the creationg of the App that is setting the MainPage to our AppShell and also has additional events similar to our page, but for the app such as when it started, went to sleep (aka background), and resumed.

The AppShell.xaml houses the Shell of our application structure. It includes styles and colors for our app shell, and then has additional items for tabs, flyouts, and pages that are displayed. Here we will find two FlyoutItem elements that represent the AboutPage and the BrowsePage. Additionally, it has a MenuItem to display a login/logout button. The ShellContent has a binding to our page that we created and automatically the pages will appear and navigate to each of them when selected.

Flyout Menu on Android

There is a lot more to Xamarin.Forms Shell and creating apps with Xamarin, but hopefully this puts you on the starting path to getting going with your app. Be sure to follow the .NET dev.to account for more entries in this series.

Learn More

There is a lot to learn, but a lot of amazing resources to get you going building apps with Xamarin. Here is a great list to go through:

]]>
Enabling C# 9 in Xamarin & .NET Standard Projects https://staging.montemagno.com/enabling-c-9-in-xamarin-net-standard-projects/ 5fb7f97f36813e0039c2d3b4 Fri, 20 Nov 2020 17:34:13 GMT Whoa! .NET 5 is out and C# 9 is out! This means you are ready to use records, new pattern matching, and more! But wait a second... your apps haven't been upgraded to .NET 5 yet or are a project type that don't support .NET  so you don't have access to the new C# 9 features :( Or do you!?!?!?! Yes, of course you do! Most new features in C# are compiler based and not necessarily runtime based.  When you create a new project the compiler determines a default C# language based on these rules:

Target framework version C# language version default
.NET 5.x C# 9.0
.NET Core 3.x C# 8.0
.NET Core 2.x C# 7.3
.NET Standard 2.1 C# 8.0
.NET Standard 2.0 C# 7.3
.NET Standard 1.x C# 7.3
.NET Framework all C# 7.3

So, if you are using .NET Standard 2.0 (the default for Xamarin.Forms) you only have access to C# 7.3 by default, which is sad. However, you can override the default inside of the .csproj. Simply open it up and add the property group:

<PropertyGroup>
   <LangVersion>preview</LangVersion>
</PropertyGroup>

You can also pick the specific version of C# that you would like:

<PropertyGroup>
   <LangVersion>9.0</LangVersion>
</PropertyGroup>

Now you are ready to start using C# 9 and convert all those classes over to records... almost! I say almost because there is one quirk with enabling C# 9 in non-.NET 5 project and when you use a record you will be granted with a System.Runtime.CompilerServices.IsExternalInit is no defined or imported error.

This can easily be fixed by adding the following code into any file. I like to add it into the AssemblyInfo.cs file:

namespace System.Runtime.CompilerServices
{
    public class IsExternalInit { }
}

There you go! now you can start using all those new fancy features!! Checkout these docs:

]]>