Key technologies

Xamarin, Microsoft Azure App Service, Prism, Xamarin.Forms, Windows Presentation Foundation (WPF)

Core team

DNV GL:

  • Matthew Chequer – Team Lead and Developer
  • Brendan Caylor – Developer
  • James Wilkinson – Developer
  • Max Palmer – Architect (not present at the hackfest)

Microsoft:

Customer profile

DNV GL is an international classification society, a certification body, and a trusted partner of companies in Maritime, Oil & Gas, Energy, Business Assurance, and Software worldwide. DNV GL operates in over 100 countries with 15,000 professional consultants. They aim to attain their 2020 vision that entails “leading towards a digital, agile, and efficient future.” To do so, they strive to find new business models so that they can face the challenge of a world that becomes more and more transparent, and where information is freely available.

The team participating in this engagement is based in Bristol, UK. They develop and manage software, tools, and products in the Energy section of DNV GL.

The focus area for this hackfest was an application suite called WindInspector, which will be used to orchestrate wind turbine inspections. The application usage spans from managing and planning inspections, to data acquisition, data management, and report generation.

Problem statement

Wind turbines are located in very remote and windy locations, and the inspectors often find themselves working under challenging conditions. They are offline for several weeks at a time, doing inspections high above ground and relying only on the equipment they are able to carry when attached to their climbing gear. Currently, the inspectors record their findings on a piece of paper that they input to a web application upon returning to the office. Needless to say, this is a tedious process that modern cloud application development can help improve.

Inspection

One of the developers, Brendan Caylor, explained their main challenge very well during his presentation on the last day of the hackfest:

“We’ve got some interesting challenges around a very, very complex data model that has to be stored offline, synchronized, and also synchronized with other devices. And all of this will happen in a disconnected state.”

The working conditions of the wind turbine inspectors resulted in the WindInspector development team having to fulfill a set of very specific requirements when architecting the app:

  • Before traveling to an inspection site, the lead inspector will create, plan, and manage all the information necessary to conduct a set of inspections. This is done in a desktop application, and the information will be synced to a set of tablets that will be used by the inspectors.
  • When the inspectors arrive at the wind turbine site, they each receive a tablet from the lead inspector. Using a Xamarin app preinstalled on the tablet, they will input and document their findings during the inspections.
  • An inspection project can last for several weeks, resulting in 200 photos and 10–20 findings on average per inspection. The lead inspector must be able to consolidate all the data collected during the inspections on site while being offline. This is done by connecting the tablets to the desktop app and doing a file transfer of the data.
  • Having transferred the findings to the desktop app, the lead inspector can begin working on the report while still being on site.
  • After returning to the office, the lead inspector can sync all the images and data to the cloud to make it accessible to all project stakeholders.

Solution Overview

In an ideal world, both the mobile/tablet app and the desktop application in the previous solution overview diagram would have been developed by using Xamarin, enabling code sharing across the Universal Windows Platform (UWP), Android, and iOS. In the case of the WindInspector project, this is not possible because the desktop application will have to support Windows 7, and UWP only runs on Windows 10. The WindInspector suite therefore consists of two separate apps: one Xamarin app for the tablets, and a Windows Platform Foundation (WPF) desktop application that connects to the Mobile Apps backend.

Architecture Overview

This architecture creates several challenges that were addressed during this hackfest.

Challenge #1: The Mobile Apps feature of Azure App Service does not support WPF as a platform

The WPF application will authenticate and authorize its users against the Azure Active Directory (Azure AD) identity provider built into the Mobile Apps feature of Azure App Service. This is not officially supported.

Challenge #2: The managed Client SDKs do not support WPF as a platform

The WPF application will operate with an offline data set that can be automatically synchronized with the backend data, including conflict resolution support. To accomplish this, the Azure Mobile Client SDKs are used, which do not provide official support for WPF.

Challenge #3: The tablet app does not support an iOS version of the Xamarin app

Before the hackfest, the developers had created the Android version of the tablet app using Xamarin.Forms. The project stakeholders wanted the app to support iOS as well, but due to time constraints the developers had not been able to prioritize this.

Solution, steps, and delivery

On the first day of the hackfest, we (the team from DNV GL and the team from Microsoft) sat down for several hours to get to know each other and get a clear overview of the existing project progress and its challenges. We had covered the basics of the project and our goals for the hackfest during a few Skype calls in advance, but we all felt it would be useful to reiterate through this face-to-face, in a setting where we had no time constraints and where we were able to ask any questions that came to mind.

During this discussion, we listed our hackfest goals on a whiteboard and decided on how we wanted to work together as a team. We determined that we would all work in the same room, but we would split the tasks among ourselves and have regular stand-up meetings throughout the hackfest to report on progress and any issues encountered.

Hackfest goals

Before tackling the three main challenges listed earlier, the project team asked the Microsoft engineers to do a technical review of the project architecture and the Xamarin code base to ensure that the foundation on which we continued our work was a strong one. Both reviews passed with top marks, giving us the green light to begin working on our three main challenges.

Challenge #1: Mobile Apps do not support WPF as a platform

Because Mobile Apps don’t support WPF as a platform, we decided to create a proof of concept (POC) with an empty WPF app to see if we were able to integrate it with Mobile Apps. The goal of the POC would be to authenticate users against the Azure AD identity provider built into Mobile Apps.

The POC had to fulfill the following requirements to be considered a success:

  • No storing of tokens in appsettings or similar; we wanted the token to be provided for us through the authentication process.
  • Authenticating the user against Azure AD and receiving a token to use against Mobile Apps should be done in one step; we don’t want to first authenticate the user and then ask for a token.

There are two possible approaches to Azure AD authentication through App Service:

  • Server-flow: Let the Mobile Apps client SDK sign in users with a single line of code.
  • Client-flow: Use the Microsoft Azure Active Directory Authentication Library (ADAL) to establish the identity and then gain access to App Service through a token received by ADAL.

The question was whether the first approach (server-flow) would work in a WPF app or whether client-flow authentication would be the best choice. For more information about the difference between server-flow and client-flow authentication, see Adrian Hall’s overview of Authentication Concepts.

After a lot of trial and error, we concluded that we wanted the WPF app to seamlessly open an Azure AD sign-in window and ask users to authenticate before switching them back to the app. The way to achieve this would be through a client-flow. Authentication Flow

Getting the client-flow authentication to work, though, turned out to be more difficult than we had anticipated. This was not due to technical reasons, but was due to the fact that there are so many different ways of handling authentication. A clear understanding of how each one works and the differences between them was needed in order to find the correct solution. One of the important lessons we had to learn was whether you are doing a client-flow or server-flow, you need to set up the server-flow first. So you are in fact not choosing between client-flow and server-flow; you are deciding whether you want the client-flow in addition to the server-flow.

The following code ensured that all our requirements for the POC were satisfied. There was no need to store any tokens in appsettings or similar, and we were authenticating the user against Azure AD and receiving the token in one step in the client-flow. Most importantly, we were able to conclude that it is possible to authenticate a WPF application and authorize its users against the Azure AD identity provider built into Mobile Apps, even though it is not officially supported.

// Client-flow authentication using ADAL  
AuthenticationContext authContext = new AuthenticationContext(authorityUri);
var authResult = await authContext.AcquireTokenAsync(
      resourceId, /* The resource we want to access  */
      clientId, /* The Client ID of the Native App */
      redirectUri,
      new PlatformParameters(PromptBehavior.Auto));

// Authenticate towards the web api using the token received from the client-flow
var client = new MobileServiceClient(appServiceUrl);
JObject payload = new JObject();
payload.Add("access_token", authResult.AccessToken);
var user = await client.LoginAsync(
      MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory,
      payload);

The team from England had brought tea and biscuits as a gift for the rest of the team, so we took some time to celebrate our success!

Tea and biscuits

Challenge #2: The managed Client SDKs do not support WPF as a platform

Because the WindInspector suite relies heavily on data being stored and managed offline for long periods of time until the inspectors return to an area where they have a network connection, we wanted to make sure the offline data sync capabilities in the Azure Mobile Client SDK would work in a WPF application although this support is not officially provided. In this case, “not supported” means that the engineering team does not have end-to-end tests for WPF, so even though the Client SDK might work for WPF now, there is no guarantee that it will continue to work in future releases of the SDK.

We decided to create a set of integration tests to verify that the offline data sync in Mobile Apps would work in a WPF application. These integration tests could later on be used as a learning tool for the other members of the team, to help them understand the concepts around offline data sync in Mobile Apps. The integration tests can also be used as a means of verifying that new versions of the Client SDK won’t break the existing behavior in WPF and that their data model is optimized for offline data sync.

Using SQLite as a local store for the WPF app, we wanted to verify that we could initialize the local store from the WPF app, execute all CRUD (Create, Read, Update, Delete) operations towards the sync table, and synchronize the changes with the Mobile Apps backend. Offline data sync

The development team from DNV GL had prepared a set of scenarios that they wanted to create integration tests for. The integration tests were aimed at investigating the following synchronization scenarios:

  • Are we able to create, delete, or edit a record on the client, without any dependencies, and sync it to the server?
  • Are we able to create a record with dependencies and sync it, without there being issues around the order in which the record and its dependencies are synced?
  • Will a record be properly merged if changes have been made to both on the server and on the client, or will the team need to implement their own conflict resolution?
  • What happens if someone deletes a record that has several other dependencies? Will the existing data model enforce a proper handling of this?
  • What happens if someone deletes a record on the server, and one of the offline clients has made changes to this record before syncing?

Let us take a closer look at one of these scenarios and the integration test that was written for it. Offline data sync scenario

In this scenario we are working with a simple record that has no dependencies. This means that we don’t have to worry about records being synchronized in the wrong order because we’re only dealing with a single record. The only thing we need to worry about is whether the record is synchronized correctly across the two WPF clients and the Mobile Apps backend.

In the following test, you can see that we need to create the TestClients and their sync tables before we can initialize their sync contexts. This can be moved to a setup method within the test class, but for readability, this is now part of the test itself.

        [Test]
        public async Task DeleteAndSynchronizedRecordWithNoDependencies()
        {
            // Create the first client and initialize the sync table
            TestClient client1 = new TestClient(AppServiceUrl, Database1FileName);
            client1.MobileServiceSQLiteStore.DefineTable<Manufacturer>();
            await client1.MobileServiceClient.SyncContext.InitializeAsync(client1.MobileServiceSQLiteStore);

            // Insert a new row on the client and assert that there exists exactly 1 record
            IMobileServiceSyncTable<Manufacturer> manufacturerSyncTable = client1.MobileServiceClient.GetSyncTable<Manufacturer>();
            Manufacturer manufacturer = new Manufacturer { ManufacturerName = "Contoso" };
            await manufacturerSyncTable.InsertAsync(manufacturer);
            Assert.AreEqual(1, manufacturerSyncTable.ReadAsync().Result.Count());

            // Push client changes to the server
            await client1.MobileServiceClient.SyncContext.PushAsync();
            
            // Create a second client, do a pull and verify that the record exists
            TestClient client2 = new TestClient(AppServiceUrl, Database2FileName);
            client2.MobileServiceSQLiteStore.DefineTable<Manufacturer>();
            await client2.MobileServiceClient.SyncContext.InitializeAsync(client2.MobileServiceSQLiteStore);
            await client2.MobileServiceClient.SyncContext.PushAsync();
            IMobileServiceSyncTable<Manufacturer> manufacturer2SyncTable = client2.MobileServiceClient.GetSyncTable<Manufacturer>();
            await manufacturer2SyncTable.PullAsync("allManufacturers", manufacturer2SyncTable.CreateQuery());
            Assert.AreEqual(1, manufacturer2SyncTable.ReadAsync().Result.Count());

            // Delete the record on client2 and push it to the server
            await manufacturer2SyncTable.DeleteAsync(manufacturer);
            await client2.MobileServiceClient.SyncContext.PushAsync();

            // Pull all manufacturers from the server to Client1 and verify that there are none
            await manufacturerSyncTable.PullAsync("allManufacturers", manufacturerSyncTable.CreateQuery());
            Assert.IsEmpty(manufacturerSyncTable.ReadAsync().Result);
        }

Through the integration tests, we were able to verify that the offline data sync capabilities in the Azure Mobile Client SDK work as expected in a WPF application, although this support is not officially provided. The most important aspect of these tests was to de-risk the offline data sync scenarios, and in that we succeeded.

The developers from DNV GL will build upon the initial tests created during the hackfest to cover more complex conflict resolution scenarios and image synchronization in their WPF application.

Challenge #3: The tablet app does not support an iOS version of the Xamarin app

Due to a tight deadline for their WindInspector application suite, the development team had to prioritize the Android version of their tablet app although the project stakeholders also had an iOS version of the app on their wish list. Because the app is built with Xamarin.Forms and contains very little platform-specific UI code, Etienne wanted to show how easy it would be to implement an iOS version in a matter of a few hours.

The tablet app is implemented with the MVVM pattern, using the Prism framework. Prism is useful for building loosely coupled, well-structured, and maintainable XAML apps. Prism comes with a set of container-specific packages, enabling developers to use their favorite IoC containers. The WindInspector team had selected Prism.Unity for their app.

In the following code, inheriting from Prism.Unity.PrismApplication enables you to get direct access to the underlying Unity IoC container and the registered NavigationService. When the app is initialized, it will navigate to the MainPage using a GenericNavigationPage. The GenericNavigationPage inherits from the Xamarin.Forms.NavigationPage Class, which manages the navigation through a stack of pages, allowing you to customize the navigation user experience.

using Prism.Unity;
using System;
using System.Diagnostics;
using WindInspector.TabletApp.Models;
using WindInspector.TabletApp.Views;

namespace WindInspector.TabletApp
{
    public partial class App : PrismApplication
    {
        public App(IPlatformInitializer initializer = null) : base(initializer) { }

        protected override void OnInitialized()
        {
            InitializeComponent();

            await NavigationService.NavigateAsync("GenericNavigationPage/MainPage");
        }

        protected override void RegisterTypes()
        {
            Container.RegisterTypeForNavigation<MainPage>();
            Container.RegisterTypeForNavigation<CheckListPage>();
            Container.RegisterTypeForNavigation<GenericNavigationPage>();
            Container.RegisterTypeForNavigation<InspectionPage>();
            Container.RegisterTypeForNavigation<MasterDetailInspectionsPage>();
            Container.RegisterTypeForNavigation<MasterHomeContentPage>();
            Container.RegisterTypeForNavigation<TabbedInspectionPage>();
            Container.RegisterTypeForNavigation<FindingsPage>();
            Container.RegisterTypeForNavigation<CoveragePage>();
            Container.RegisterTypeForNavigation<TurbinePage>();
            Container.RegisterTypeForNavigation<ComponentPage>();
            Container.RegisterTypeForNavigation<MainComponentsPage>();
            Container.RegisterTypeForNavigation<SearchComponentPage>();
            Container.RegisterTypeForNavigation<FindingPage>();
        }
    }
}

One challenge we faced when creating the iOS version of the app was that, although it compiled successfully, it threw an error when launching on an iOS device. The error message read “Root element is missing” and did not provide any additional details. The fact that this error is often seen in relation to XML documents led us to look at the XAML views of the page that were loading as the app was initialized. From the previous code snippet, we see that the MainPage is loaded within the GenericNavigationPage, so one of these had to be the problem. Through trial and error, Etienne found that adding an empty StackLayout element to the GenericNavigationPage.xaml page made the error disappear, as shown in the following code:

<?xml version="1.0" encoding="utf-8" ?>
<NavigationPage xmlns="http://xamarin.com/schemas/2014/forms"
                xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
                prism:ViewModelLocator.AutowireViewModel="True"
                x:Class="WindInspector.TabletApp.Views.GenericNavigationPage"
                Title="GenericNavigationPage">
  <StackLayout></StackLayout>
</NavigationPage> 

The team now had a working iOS version of the application!

Team working hard

Conclusion

The main goal of this engagement was to remove any risk involved in the WindInspector application suite, providing the DNV GL development team with the certainty that the decisions they had made were the correct ones. The project has very specific requirements regarding Windows 7 support and the offline capabilities of the applications, resulting in an architecture containing a WPF desktop application and a Xamarin.Forms tablet application, both working with a Mobile Apps backend.

During the hackfest, we were able to verify that the WPF application can authenticate and authorize its users against the Azure AD identity provider built into Mobile Apps, although this is not officially supported. We were also able to verify that the managed Client SDKs can be used in a WPF application for data synchronization towards Mobile Apps. These two verifications were very important to the DNV GL team as they had set the foundation for the architecture of the application suite.

The DNV GL team will continue their work on the WindInspector application suite, aiming at a launch later this year. Planning forward, they see the opportunity of supporting iOS and introducing automated build, testing, and distribution to their mobile app development process.

We (Microsoft) would like to thank the DNV GL team for five great days of hard work, play, and impressive team work. Thank you!

Additional resources