Building a Data Economy with UWP and App Services

Data is one of the most important drivers for building a great application. We often call various web services to get the information we require to build a wonderful user experience, but we find that more apps are making similar web service calls to fetch the same information - weather is a classic example.

On my device I have a few apps that use weather data to enhance the experience, there is the weather app, a calendar app, a hiking app and a cycling app. All these applications want the same information, however, they are all making network calls and most importantly using my battery life. Imagine if the weather application fetched the weather data and then the other apps query the weather application for the information instead - even though it's from another publisher. Well, that's what App Services does, it's a way to create mini web service like calls between applications.

To demonstrate App Services I am going to create 2 applications, the first one a grocery shop - the publisher - and the second a notes application - the consumer. The grocery shop application stores the prices of fruit that are available. The notes application allows the user to create a shopping list and fetch pricing information from the grocery shop application allowing me to stay within my $20 budget.


The Grocery Shop a.k.a The Publisher

I know it's not much to look at, but most of the exciting stuff happens when this application is closed.

The first thing we do is add a new Windows Runtime Component project, so right click on the solution and add this new project.

Rename the Class1.cs file, in this case I renamed it to Grocery.cs, make sure it's marked as sealed and implements the IBackgroundTask interface.

Your code should look something like this.

public sealed class Grocery : IBackgroundTask
{
    public void Run(IBackgroundTaskInstance taskInstance)
    {
    }
}

Now let's add the code for the Run method.

public sealed class Grocery : IBackgroundTask
{
    private BackgroundTaskDeferral deferral;
    private AppServiceConnection appServiceConnection;

    public void Run(IBackgroundTaskInstance taskInstance)
    {
    this.deferral = taskInstance.GetDeferral();

    taskInstance.Canceled += TaskInstance_Canceled;

    var trigger = taskInstance.TriggerDetails as AppServiceTriggerDetails;
    this.appServiceConnection = trigger.AppServiceConnection;
    this.appServiceConnection.RequestReceived += AppServiceConnection_RequestReceived;
    }
}

You can see I ask for a deferral, to make sure that my background process isn't terminated early. I then wire up a Cancelled event, just in case the background work has a cancellation request. Finally, I extract the AppServiceConnection - which is basically the connection string of the app services. The AppServiceConnection has a RequestRecieved event, think of it like a HTTP request/response scenario.

private void TaskInstance_Canceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
    if (this.deferral != null)
    {
        this.deferral.Complete();
    }
}

The cancelled event code is pretty simple, I just check to see if the deferral is not null if so I call Complete() so it released.

The AppServiceConnection_RequestReceived event is where all the work is done.

private Dictionary groceries = new Dictionary { { "Apples", "$799" }, { "Oranges", "$1" }, { "Bananas", "$1.99" }, { "Pears", "$0.50" } };

private async void AppServiceConnection_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
    var deferral = args.GetDeferral();
    var requestMessage = args.Request.Message;
    var responseMessage = new ValueSet();

    foreach (var item in requestMessage)
    {
        if (groceries.ContainsKey(item.Value.ToString()))
        {
            responseMessage.Add(item.Value.ToString(), groceries[item.Value.ToString()]);
        }
    }

    await args.Request.SendResponseAsync(responseMessage);

    deferral.Complete();
}

Again, I get the deferral to make sure the method isn't cut short. As mentioned earlier, think of App Services like a HTTP request / response, I first look at the request and then build out a response.  The mechanism for passing data between App Services is to use a ValueSet - basically a list of key value pairs. In this case the key for each value pair is an int and the value is the fruit whose price is being requested. For the purpose of this application I've created a dictionary with hard coded values these are read when building my response. Now I have a ValueSet with my response, I send the response back to the caller by calling the SendResponseAsync method passing my response. Once the call has completed, I call Complete() on the deferral.

Now that we have written all the code for our background process we need to link it to the main Grocery Shop application. To do this we first need to add a reference to the background task, then we need to update the package manifest by double clicking it.

You add a new App Service declaration - note it's under it's own declaration and not a background task.

We then fill out the key information, the first being the name, which has to be unique across the device, so a reverse domain is normally recommended. Second is the entry point, the place that will get called to perform the action. You have to specify a fully qualified namespace with class, so in my case it's GroceryShop.Background.Grocery


Notes application a.k.a The Consumer

Now that we have an application that can respond to request, we need to create an application that can create requests. This where the Notes application is useful.

notes.PNG

The user types in the information in the text box on the left and clicks the Parse button. The data will be fetched from the Grocery Shop application and populates an Items observable collection which is data bound to the ListView on the right.

Let's take a look at the code for the ParseButton_Click event.

private async void ParseButton_Click(object sender, RoutedEventArgs e)
{
    this.Items.Clear();

    this.appServiceConnection = new AppServiceConnection
    {
        AppServiceName = "com.shenchauhan.GroceryShop",
        PackageFamilyName = "292c6a2f-3028-412d-b530-23fbc868d2cb_8gdmnpqbw06hm"
    };

    var status = await this.appServiceConnection.OpenAsync();

    switch (status)
    {
        case AppServiceConnectionStatus.AppNotInstalled:
            await LogError("The Grocery Shop application is not installed. Please install it and try again");
            return;
        case AppServiceConnectionStatus.AppServiceUnavailable:
            await LogError("The Grocery Shop application does not have the available feature");
            return;
        case AppServiceConnectionStatus.Unknown:
            await LogError("Uh-oh! erm.....");
            return;
    }

    var items = this.NotesTextBox.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);

    var message = new ValueSet();

    for(int i=0; i< items.Length; i++)
    {
        message.Add(i.ToString(), items[i]);
    }

    var response = await this.appServiceConnection.SendMessageAsync(message);

    switch (response.Status) {
        case AppServiceResponseStatus.ResourceLimitsExceeded:
            await LogError("Yikes! The resource for this device exceeded the limits so the app service was shutdown");
            return;
        case AppServiceResponseStatus.Failure:
            await LogError("Oh dear, we failed to get a response");
            return;
        case AppServiceResponseStatus.Unknown:
            await LogError("uh-oh... Not sure why we didn't get a response");
            return;
    }

    foreach (var item in response.Message)
    {
        this.Items.Add(new ShoppingItem
        {
            Name = item.Key,
            Price = item.Value.ToString()
        });
    }
}

The first thing that needs to be done is create an AppServiceConnection, the details require the name and the package family name. The app service name comes from the name we declared in the package manifest in the Grocery Shop app. To get the package family name is a little more trickier, you can either read the value of Package.Current.Id in the Grocery Shop app or when you deploy the Grocery Shop app you will see in the output window the package name, to get the family name you will need to remove the architecture and version number from this.

We have now setup the connection and the next thing we need to do is open it - we do this by calling OpenAsync on the appServiceConnection. This call will return a AppServiceConnectionStatus, I check for various failures - e.g. the application that I'm trying to contact is not installed.

They're a few lines of code where I'm taking text from the NotesTextBox and then creating a ValueSet, remember a ValueSet is a like a list of key value pairs which is used to communicate between apps using app services.

Once I have the request setup I send the data to the Grocery Shop application by calling the SendMessageAsync method passing the ValueSet as an argument. This call returns a response status so we know if the request was successful. If the response was successful I look at the returned ValueSet using the Message property and populate the Items collection, which in return populates the list.


Debugging an App Service

To debug an app service, simply view the debug settings on the UWP project that host the app service - the Grocery shop in this example - and set the "Do not launch, but debug my code when it starts." Then, make sure the Grocery Shop is set as the start up project, hit F5 to debug and then launch the consuming application from the start menu.


The final result is below.

As you can see, this App Services is a powerful way for your app to either publish or consume data from other apps installed on the device. If you are a publisher, it's a good idea to document what App Service calls are valid and how consumer apps connect to them, maybe create a library/nuget package to make it more consumable.

The code can be found on GitHub: https://github.com/shenchauhan/blog/tree/master/AppServices

Channel 9 video: https://channel9.msdn.com/Shows/Inside-Windows-Platform/Exposing-and-Calling-App-Services-from-your-UWP-app

I can't wait to see apps using this powerful feature. Happy Coding!