C#

Wroc# – developer conference worth attending

Wroc# - audience

Earlier this year I had a pleasure to speak at Wroc# conference in Poland. It was very well organized event that was almost free for attendees. The only cost was required donation to charity: PLN 150 (~$50).

There was only 1 track with awesome speakers lineup! I finally had an opportunity to meet Roy Osherove in person. I learned about unit testing from his book The Art of Unit testing. I’m currently reading his another book: Elastic Leadership – a lot of useful tips not only for team leaders! Among other speakers there were Scott Helme (Security Researcher) that uncovered some things about web security I have never heard about! Zan Kavtaskin gave great overview about building apps on Azure Cloud, Glenn Henriksen showed how on server-less computing works in Real-World, and Sander Hoogendoorn together with Kim van Wilgen shared their perspective on over-engineering development processes.

The conference venue was great. I would say it was the best setup I’ve even seen! There was one big room, divided into 3 parts: stage, chairs for audience and Mix&Mingle zone (AKA M&M). You could talk (in the Mix&Mingle zone), and still be able to follow presentations. Speakers’ room was on the upper floor, but it was more like a balcony, from where you could listen to talks and overlook entire venue.

I delivered talk about building mobile apps with Xamarin. I shared what we have learned while building Azure Mobile App, which started as hackathon project, and later turned into official Microsoft product. The app got announced on the stage of //build conference last year. Along the way we learned how to properly architect Xamarin project for multiple platforms, where to do not take shortcuts, does and don’ts for CI/CD and testing.

There was a guy who put a nice summary of my talk:

Wroc# - building mobile apps with Xamarin

At the end of the conference there was speaker’s panel where we were answering and discussing questions from the audience. We had good discussion about different aspects of software development from estimating project cost to writing unit tests. Almost every speaker had different background, and this made it even more interesting!

Wroc# - speakers panel

If you haven’t been to Poland before: Wroclaw is an amazing city, and many of my foreign friends says it’s their favorite city in Poland. Wroclaw is often refereed as WrocLove ūüėČ

WrocLove

Last, but not least: thank you for everyone who made this conference happen!

Wroc# - organizers


Adding biometrics authentication to Xamarin.iOS (Touch ID / Face ID) and Xamarin.Android (Fingerprint)

One of the top Azure App users requests was to add Touch ID support for additional security. In this post I will share the details of implementing biometrics authentication for iOS and Android with Xamarin.

There are three aspects of biometrics auth:
1. Enable user to turn biometrics authentication on and off. Users shouldn’t be forced to use this additional security feature.
2. Detecting when user should be asked for biometrics authentication, e.g., when app is coming from background, and when app is starting.
3. Authentication process. Includes detecting hardware capabilities (is touch or face id available?), and local setup (does user configured local authentication in system settings).

Enabling biometrics authentication usually can be controlled in settings (like in Outlook or OneDrive). We did the same in Azure App:

Require Touch ID Settings

iOS

Detecting when user is switching back to our app in iOS is pretty simple. Every time when user switch from background, method WillEnterForeground in AppDelegate is being called. We just need to override it with our custom implementation:

public override void WillEnterForeground(UIApplication application)
{
    // biometrics authentication logic here
}

You should also authenticate user when app is being launched. In that case authentication should be performed in your initial view controller.

In iOS we have 2 kinds of biometrics authentication:
1. Touch ID
2. Face ID (available from iPhoneX)

We can also fallback to passcode if touch/face ID is not configured, or user’s device does not support it.

The iOS Local Auth API is pretty straightforward, and well documented. I created simple helper to handle feature detection and authentication:

public static class LocalAuthHelper
{
    private enum LocalAuthType
    {
        None,
        Passcode,
        TouchId,
        FaceId
    }

    public static string GetLocalAuthLabelText()
    {
        var localAuthType = GetLocalAuthType();

        switch (localAuthType)
        {
            case LocalAuthType.Passcode:
                return Strings.RequirePasscode;
            case LocalAuthType.TouchId:
                return Strings.RequireTouchID;
            case LocalAuthType.FaceId:
                return Strings.RequireFaceID;
            default:
                return string.Empty;
        }
    }

    public static string GetLocalAuthIcon()
    {
        var localAuthType = GetLocalAuthType();

        switch (localAuthType)
        {
            case LocalAuthType.Passcode:
                return SvgLibrary.LockIcon;
            case LocalAuthType.TouchId:
                return SvgLibrary.TouchIdIcon;
            case LocalAuthType.FaceId:
                return SvgLibrary.FaceIdIcon;
            default:
                return string.Empty;
        }
    }

    public static string GetLocalAuthUnlockText()
    {
        var localAuthType = GetLocalAuthType();

        switch (localAuthType)
        {
            case LocalAuthType.Passcode:
                return Strings.UnlockWithPasscode;
            case LocalAuthType.TouchId:
                return Strings.UnlockWithTouchID;
            case LocalAuthType.FaceId:
                return Strings.UnlockWithFaceID;
            default:
                return string.Empty;
        }
    }

    public static bool IsLocalAuthAvailable => GetLocalAuthType() != LocalAuthType.None;

    public static void Authenticate(Action onSuccess, Action onFailure)
    {
        var context = new LAContext();
        NSError AuthError;

        if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out AuthError)
            || context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthentication, out AuthError))
        {
            var replyHandler = new LAContextReplyHandler((success, error) =>
            {
                if (success)
                {
                    onSuccess?.Invoke();
                }
                else
                {
                    onFailure?.Invoke();
                }
            });

            context.EvaluatePolicy(LAPolicy.DeviceOwnerAuthentication, Strings.PleaseAuthenticateToProceed, replyHandler);
        }
    }

    private static LocalAuthType GetLocalAuthType()
    {
        var localAuthContext = new LAContext();
        NSError AuthError;

        if (localAuthContext.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthentication, out AuthError))
        {
            if (localAuthContext.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out AuthError))
            {
                if (GetOsMajorVersion() >= 11 && localAuthContext.BiometryType == LABiometryType.TypeFaceId)
                {
                    return LocalAuthType.FaceId;
                }

                return LocalAuthType.TouchId;
            }

            return LocalAuthType.Passcode;
        }

        return LocalAuthType.None;
    }

    private static int GetOsMajorVersion()
    {
        return int.Parse(UIDevice.CurrentDevice.SystemVersion.Split('.')[0]);
    }
}

There are helper methods determining proper label (GetLocalAuthLabelText), icon (GetLocalAuthIcon) and authentication text (GetLocalAuthUnlockText) depending on available authentication type. There is also one liner IsLocalAuthAvailable checking if Local Authentication (face/touch ID or passcode) is available, and Authenticate method that performs authentication, which takes success and failure callbacks as parameters. It can be used in WillEnterForeground method as follows:

public override void WillEnterForeground(UIApplication application)
{
    if (!AppSettings.IsLocalAuthEnabled)
    {
        return;
    }

    LocalAuthHelper.Authenticate(null, // do not do anything on success
    () =>
    {
        // show View Controller that requires authentication
        InvokeOnMainThread(() =>
        {
            var localAuthViewController = new LocalAuthViewController();
            Window.RootViewController.ShowViewController(localAuthViewController, null);
        });
    });
}

We do not have to do anything on success. The popup shown by iOS will disappear and user will be able to use the app. On failed authentication though we should display some kind of shild (e.g., ViewController) that prevent user from using the app until authorization succeed. This is how it looks in Azure App:

Azure App - Unlock with Touch ID

Android

Detecting when app is coming from background in Android is tricky. There is no single method that is invoked only when app is coming back from background. The OnResume method is being called when app is coming back from the background, but it’s also called when you switch from one activity to another. Solution for that is to keep a time stamp with last successful authentication, and update it to DateTime.Now every time when activity is calling OnPause. This happen when app is going to background, but also when app is changing between activities. Thus we cannot simply set flag Background=true when OnPause is called. However, when difference between subsequent OnPause and OnResume is larger than some period of time (e.g., more than a few seconds) we can assume that app went to background. Below code should be implemented in some BaseActivity class that all activities inherit from:

public class BaseActivity
{
  public const int FingerprintAuthTimeoutSeconds = 5;
  public static DateTime LastSuccessfulFingerprintAuth = DateTime.MinValue;
    
  protected override void OnResume()
  {
    base.OnResume();

    if (IsFingerprintAvailable() && LastSuccessfulFingerprintAuth > DateTime.Now.AddSeconds(-FingerprintAuthTimeoutSeconds))
    {
      StartActivity(typeof(FingerprintAuthActivity));
    }
  }

  protected override void OnPause()
  {
    base.OnPause();

    if (IsFingerprintAvailable())
    {
      LastSuccessfulFingerprintAuth = DateTime.Now;
    }
  }
}

The basics of Fingerprint authentication are very well described in Xamarin docs.

Even better reference is a sample app FingerprintGuide from Xamarin.

The main disadvantage of adding fingerprint authentication in Android (over Face/Touch ID in iOS) is requirement to build your own UI and logic for the authentication popup. This includes adding icon, and handling all authentication results. iOS handles incorrect scan, and displays popup again with passcode fallback after too many unsuccessful tries. In Android you have to implement this entire logic by yourself.

Summary

Adding biometrics authentication is useful for apps that hold sensitive data, like banking apps, file managers (Dropbox, OneDrive), or an app that has access to your Azure Resources ūüôā

Implementing local authentication in iOS is pretty straightforward, and iOS APIs provide authentication UI for free. In Android however, the APIs are only working with the backend, and UI has to be implemented by you.

Local authentication should be always optional. Some users may not need nor want it. Thus, it should be configurable in the app settings.

Try out biometrics auth in Azure App!

Download on the App Store
Get it on Google Play


In-memory caching in Xamarin apps

CPU

Recently we added in-memory caching to Azure App. You can try it out now on iOS and Android!

It turns out Mono doesn’t have System.Runtime.Caching namespace, which makes it easy to implement caching for .NET apps. We had to find another way.

Caching libraries for Xamarin

We looked at a few libraries for caching (e.g., MemoryCache and Akavache), but surprisingly none of them manage cache size and memory. They simply add items to Dictionary, and if you add too many you get OutOfMemoryException.

It may not be an issue for many applications, but in Azure App we need to take into account users who has multiple subscriptions with thousands of resources.

BTW: Akavache is a great library. Besides in-memory cache it also supports persistent cache, have clean APIs and a lot of great documentation.

Implementing in-memory cache

After browsing internets and asking people at Xamarin chat we didn’t find anything that would work for us, and we decided to implement in-memory cache by ourselves.

public class InMemoryCache<T> : IInMemoryCache<T>
{
    private const int LimitedCacheThreshold = 1000;

    private class Reference
    {
        private int _hitCount = 0;

        public DateTimeOffset Timestamp
        {
            get;
            private set;
        }

        public T Data
        {
            get;
            private set;
        }

        public void AddRef()
        {
            Interlocked.Increment(ref _hitCount);
        }

        public int ResetRef()
        {
            var count = _hitCount;
            _hitCount = 0;
            return count;
        }

        public static Reference Create(T obj)
        {
            return new Reference()
            {
                Timestamp = DateTimeOffset.Now,
                Data = obj,
            };
        }

        private Reference()
        {
        }
    }

    private readonly ConcurrentDictionary<string, WeakReference<Reference>> _weakCache;
    private readonly ConcurrentDictionary<string, Reference> _limitedCache;
    private readonly ConcurrentDictionary<string, Task<T>> _pendingTasks;

    private InMemoryCache()
    {
        _weakCache = new ConcurrentDictionary<string, WeakReference<Reference>>(StringComparer.Ordinal);
        _limitedCache = new ConcurrentDictionary<string, Reference>(StringComparer.Ordinal);
        _pendingTasks = new ConcurrentDictionary<string, Task<T>>(StringComparer.Ordinal);
    }

    public static IInMemoryCache<T> Create()
    {
        return new InMemoryCache<T>();
    }

    public async Task<T> GetOrAdd(string key, DateTimeOffset expiration, Func<string, Task<T>> addFactory)
    {
        WeakReference<Reference> cachedReference;

        if (_weakCache.TryGetValue(key, out cachedReference))
        {
            Reference cachedValue;
            if (cachedReference.TryGetTarget(out cachedValue) || cachedValue != null)
            {
                if (cachedValue.Timestamp > expiration)
                {
                    cachedValue.AddRef();
                    return cachedValue.Data;
                }
            }
        }

        try
        {
            var actualValue = await _pendingTasks.GetOrAdd(key, addFactory);

            if (_limitedCache.Count > LimitedCacheThreshold)
            {
                var keysToRemove = _limitedCache
                    .Select(item => Tuple.Create(
                        item.Value.ResetRef(),
                        item.Value.Timestamp,
                        item.Key))
                    .ToArray()
                    .OrderBy(item => item.Item1)
                    .ThenBy(item => item.Item2)
                    .Select(item => item.Item3)
                    .Take(LimitedCacheThreshold / 2)
                    .ToArray();

                foreach (var k in keysToRemove)
                {
                    Reference unused;
                    _limitedCache.TryRemove(k, out unused);
                }
            }

            var reference = Reference.Create(actualValue);
            _weakCache[key] = new WeakReference<Reference>(reference);
            _limitedCache[key] = reference;

            return actualValue;
        }
        finally
        {
            Task<T> unused;
            _pendingTasks.TryRemove(key, out unused);
        }
    }
}

We use two layers of caching. First is using WeakReference that leaves memory management to Garbage Collector. As GC is not very predictable and sometimes may unnecessary release some reference, we have second layer of caching. We call it _limitedCache, and it keeps objects in memory until capacity reach 1000 objects. Then we remove half (500), least used objects from dictionary. Because the same objects are being kept in two dictionaries, the WeakReference will never be released as long as object is in _limitedCache. Thus, we always check only if object is present in _weakCache.

There is also third dictionary that keeps track of pending tasks that are responsible for getting data. This prevents us from sending the same requests more than once if object is not in cache yet.

Summary

What is great about building apps with Xamarin is the ability to share code across platforms. When we were implementing cache, we didn’t touch any platform specific code. All work was done in Portable Class Library.

Adding cache to Azure App helped not only to decrease user’s network data usage, but also to improve performance significantly!

If you need in-memory cache for your app, go ahead and use the above code snippet! If you are looking for persistent cache then consider using Akavache.

Are you caching? How? Why? Why not?


Azure Resource Manager Batch API

The latest Azure Mobile App update has statuses on the resources list:

Azure App - Statuses on resources list

You probably want to ask why we didn’t have them before. Great question! Currently Azure Resource Manager¬†(public API we are using to get your Azure resources) requires to make separate calls to get single resource status. It means: if you have 100-200 resources, you would have to make 100-200 extra calls. There are some people who has almost 2000 in one subscription! Taking performance and data usage into consideration, this is not ideal.

Both iOS and Android platforms allows to address this problem to some extent by querying for status only resources that are currently visible. However this is still extra 5-10 calls. It is even worse when you start scrolling, and very bad if you scroll on your list containing 2000 resources.

Batch API

Sometime ago ARM added Batch API Рyou can send POST request with up to 20 URIs in the body. Response will contain up to 20 packaged responses that you have to extract. Using batch API, you can decrease number of requests by up to 20x. This matters especially when user has a lot of resources and keep scrolling on the list.

When implementing batch requests, you need to figure out the optimal interval for sending requests. We started with 200ms, but then we changed it to 50ms. Additionally, every time new request is coming we delay sending batch request by additional 50ms. This may cause indefinite delay. In order to solve this: we always submit request if queue has 20 or more pending requests. 20*50ms = 1000ms = 1s = long time! We tweaked it again, and changed interval to 20ms. With current implementation, we wait anytime between 20ms and 400ms to send batch request.

Implementing Batch API

You probably gonna say: “it all sounds great, but how do I implement it”? For you convenience I created small console application that demonstrate ARM Batch API in action, and I put it on github.

Xamarin.iOS and Xamarin.Android does not have System.Threading.Timer. We created our own implementation OneShotTimer (thanks William Moy!).

Entire magic happens in ArmService. It has one public method GetResource that instead of directly sending GET request is adding request to ConcurrentQueue. OneShotTimer and BatchRequestDipatcher methods are responsible for sending the actual HTTP request.

In order to run console app, you need to provide ARM token, and (optionally) resource ids you want to request. In demo app I provided fake resource ids, which will be fine to issue requests, but you will not get resource back.

To get ARM token, go to Azure Portal, open F12 tools and inspect some ARM request. From request headers, copy Authorization header (string starting with Bearer rAnDoMcHaRacTErS...):

Azure Portal - ARM token

You can also get resources ids from F12 tab. The best way is to go to All Resources blade, and find some batch request:

Azure Portal - resources ids

Once you paste resource ids and ArmToken in Program.cs you can run the app, and you should see the following output:

Batch requests with 5s randomness

Requests are send in random time, anytime from 0 to 5s after program runs. This is done using Task.Delay:

var tasks = _resourceIds.Select(async resourceId =>
            {
                await Task.Delay(new Random().Next() % 5000);   // simulate calling GetResource from different parts of UI
                var response = await _armService.GetResource(resourceId);
                resources.Add(response);
            });

When you change randomness from 5s to 0.5s you can observe that there will be less batch requests (AKA more requests sent in single batch):

Batch requests with 0.5s randomness

Summary

Using Batch API for getting resource statuses visibly improves performance in the mobile app. It is noticeable especially when using network data.

Azure Resource Manager has plans to add ARM API that will allow to do 1 request to get multiple resources with statuses. This should improve performance even more in the future.

If you are facing similar problem with your app, consider implementing Batch API on your server!


Under the hood of the Azure Mobile App

For last 6 months I’ve been working on¬†the Azure Mobile App¬†for iOS and Android.¬†We officially¬†announced it at Microsoft’s¬†//build conference keynote last month.

Now, you can monitor your Azure Resources from your phone! You can also take quick management actions, like start/stop/restart. Actually, you can do whatever you want using Cloud Shell that enables you to execute any command available through Azure CLI and PowerShell.

To learn more about the app functionalities, check out Michael Flanakin‘s blog post: Introducing the Azure app public preview.

Xamarin

The app is built with Xamarin Native in C#. To learn more about Xamarin, checkout my blog post Getting started with Xamarin in 2016.

The great thing about Xamarin Native apps is the fact that you can do everything what is possible when building native iOS apps with swift and native Android apps with Java. You can take any code sample in swift or Java, translate it to C# and use in your Xamarin app. Additionally, you can share code across platforms. We have around 60-70% code share. Most of our code is shared using PCL (Portable Class Library). Some components are in Shared Project.

Azure Mobile Solution

Continuous Integration and Continuous Delivery with VSTS and Hockey App

VSTS¬†provide awesome tools for customizing build params, running tests, and deploying with¬†HockeyApp.¬†What’s more, when you are publishing your alpha/beta builds with¬†HockeyApp you get auto-update notifications for free.

We have 3 environments:

  • alpha – deployed¬†on every commit if tests are passing (used by team members)
  • beta – deployed after merge from alpha branch if all tests are passing (available for all Microsoft employees – it allows us to test the next release candidate)
  • App Store / Google Play – deployed manually (Apple does not provide mechanism¬†to auto-deploy and we are working on automating Google Play deploy from VSTS)

Build definitions

I blogged about setting up VSTS for Xamarin.iOS earlier this year. Configuring Android build is much easier. Our VSTS build pipelines for iOS and Android look like below.

VSTS Xamarin.iOS build definition

VSTS - Xamarin.Droid build definition

The general scenario is:

  • build in Debug mode (in order to initialize TestCloud, which I described in this blog post)
  • run tests
  • build in Release mode (without TestCloud init code)
  • deploy to HockeyApp

We are thinking about running tests in Release mode (it requires passing TEST_CLOUD build param to build command explicitly). This will give us the same app that later on will be deployed to HockeyApp. A few times we had situations when app was working fine in Debug mode and all UI tests were passing, but it was crashing on startup in Release mode. In this case, if somebody updated app on their device, they had to uninstall it, and install manually again after we fixed bug causing crash. Very inconvenient.

Storing secrets

VSTS provides mechanism to¬†keep your secrets¬†(passwords and tokens) outside of your source code. You can store them in Variables tab in your build definition.¬†Notice small lock next to secret value. Once you click it, it will hide the secret forever. There is no way to read it back from VSTS. I’ve done this a few times, and had to regenerate keys and tokens. You can store your secrets in Azure KeyVault, where they are always readable.

VSTS - variables

When you are running UI tests, usually you need to authenticate (AKA you need password to login with your test account). There is no way to pass password as parameter to Test Cloud, but there is a workaround: we have file Password.txt in UI test project (without password of course), and before running UI tests, we run shell script that takes password (from VSTS Variables) as parameter and writes it to the Password.txt file. You can pass variables stored in VSTS as arguments to VSTS tasks.

This is shell script:

#!/bin/bash
echo $1 > $2/Tests/AzureMobile.UITests/password.txt

And this is VSTS task:

GetPasswordForTestCloud

VSTS is awesome! It allows you to do almost anything with VSTS tasks or custom scripts. You can modify version number in Info.plist file before building solution, add release notes from git commit messages and much, much more.

UI tests with Xamarin Test Cloud

TestCloud is awesome. It allows you not only automate UI testing. It also enables you to test your app on hundreds of devices without need of buying any of them.

UI tests are usually the longest running task in our build pipeline. In order to minimize wait times between builds, we run UI tests only on one, most reliable device. We have separate build definitions to run UI tests nightly on 20 iOS and 40 Android devices. It takes longer, but it helps us to identify issues related to particular device model. Most of the time we don’t have changes that are breaking single device.

We are also thinking about creating 1-2 smoke tests that will be run on every commit, and run entire suite¬†nightly or on separate build (to don’t block other runs).

Don’t assert! Use WaitFor!

UI testing 101: when you are writing UI tests, remember to wait for UI to update before asserting some condition. E.g., let’s say you have button and label. After button click, you expect label to update its text to ‘Button clicked’. Below test will be very flakey:

  app.Tap("myButton");
  Assert.AreEqual("Button clicked", app.Query("myLabel").Label);

Label may not get updated before assertion gets executed. Instead you should use WaitFor:

  app.Tap("myButton");
  app.WaitFor(app.Query("myLabel").Label.Equals("Button clicked", StringComparison.InvariantCulture));

Page Object Pattern

Another very good practice for writing UI tests is to use Page Object Pattern. This allows you to isolate implementation details from tests scenarios.

Here is one of our tests:

[Test]
public void Favorites_AddToFavorites_ResourceVisibleOnFavoritesPage()
{
    // Arrange
    var resourceToFavorite = TestResources.Azurembwinvm;
    TapTabBarItem(Strings.Resources);

    // Act
    _resourcesPage.TapResourceByName(resourceToFavorite);
    _resourceDetailsPage.VerifyPresent();
    _resourceDetailsPage.ExecuteCommand(Strings.Favorite, Strings.Unfavorite);
    _resourceDetailsPage.GoBack();

    // Assert
    TapTabBarItem(Strings.Favorites);
    _favoritesPage.VerifyPresent();
    _favoritesPage.VerifyNavBarTitle(1);
    _favoritesPage.WaitForResourceByName(resourceToFavorite);
}

Here, are some methods implemented in Page class:

public FavoritesPage WaitForResourceByName(string resourceName)
{
    _app.Screenshot($"Waiting for resource {resourceName}...");

    _app.WaitForElement(resourceName);

    return this;
}

public FavoritesPage TapResourceByName(string resourceName)
{
    _app.Screenshot($"Tapping resource '{resourceName}'...");

    _app.WaitForAndTapOnElement(resourceName);

    return this;
}

public FavoritesPage WaitForNoResourceByName(string resourceName)
{
    _app.Screenshot($"Checking if resource '{resourceName}' is not present...");

    _app.WaitForNoElement(resourceName);

    return this;
}

Screenshots during UI tests

As you probably noticed in above code snippet, we are taking screenshots at almost every step. Thanks to Page Object Pattern we don’t have to think about it in our test steps, but only in our Page implementations. Screenshots helps a lot in diagnosing failures in TestCloud:

Xamarin TestCloud - screenshots

Above test is failing¬†when¬†Waiting for DetailsCommandLayout…¬†Thanks to screenshot, you can easily identify the line of code responsible for failure.

Monitoring and Crash Reporting with HockeyApp and AppInsights

A lot of people are using HockeyApp for crash reporting. Not many people are using it for application logging though.¬†That’s because HockeyApp does not provide a good way for¬†searching logs. However, you can create an AppInsights bridge app and stream all HockeyApp events to AppInsights.

AppInsights provides very powerful query explorer to search through your logs.

By default logs are preserved for 3 months, but you can export them to storage account and keep forever if you want.

What’s more, you can create awesome PowerBI dashboards using data from AppInsights.

Xamarin Open Source plugins and libraries

We saved a lot of time by using cross-platform Xamarin plugins:

  • XamSettings Plugin (from James Montemagno) – it is an abstraction over native data storage (SharedPreferences for Android, NSUserDefaults for iOS) that allows you to store/access local data in shared code
  • FFImageLoading (from Daniel Luberda) – very powerful image library, it allows us to convert SVGs to native formats in runtime, in shared code
  • OxyPlot (recommended by Frank Kruger) – awesome, powerful, cross-platform chart library used for metrics visualization

Xamarin community is awesome. If you are building Xamarin apps, I strongly recommend you to join slack channel XamarinChat. You can find there a lot of great developers who are willing to help. We learned about FFImageLoading there. What’s more, Daniel Luberda (FFImageLoading top contributor) is very active there. One time, he fixed a bug in less than 6h after reporting it by us ūüôā

Summary

I am really excited about this app. It is an awesome feeling to take an app from zero to //build keynote. I am even more excited because there is a lot more to come. You can help us with that! If you have an idea about what should be added/changed/removed in Azure App go to aka.ms/azureapp/feedback and add or vote for a feature.

In meantime, get the app from App Store or Google Play, and let us know what you think!

Check out me and James Montemagno chatting about the app on Xamarin Show:

*This post was written in the clouds during my flight from Seattle to Frankfurt ūüôā