Special Windows 10 issue 2015

Volume 30 Number 11

App Integration - Linking and Integrating Apps on Windows 10

By Arunjeet Singh | Windows 2015

Most app developers build or regularly maintain multiple apps. As the apps mature, users frequently demand workflows that involve the multiple apps working together. For example, maybe you have an app that manages product inventory and another app that does checkout. It would be ideal for the two apps to work together to complete a purchase workflow.

One way to solve this challenge is simply to incorporate all functionality into one app. In fact, this is an approach often seen in desktop class applications. However, this is a road fraught with peril. Soon, you end up with a bloated app where most users only use a specific subset of the functionality. The app developer must now manage both UI complexity and updates for the entire app. Even worse, as the UI complexity increases, users—especially those on mobile—begin to gravitate toward more focused options. In fact, the trend has been toward decomposing apps into individual experiences so users can install and use what they need on the go without having to worry about the extraneous bits they don’t need.

A second way to solve the problem is to leverage the cloud as a means of communication between apps. That works great until the amount of data gets larger than a certain size or you run into users with limited connectivity. This starts to show up with complaints such as, “I updated my status over here, but it doesn’t show up in this other app over there!” In addition, I’ve always found it a little strange that app developers must resort to the cloud to communicate between two apps sitting on the same device. There has to be a better way.

In this article, I’ll look at some of the tools that Windows 10 provides to make communication between apps easier. Communication between apps can take the form of an app launching another app with some data or it could mean apps just exchanging data with each other without having to launch anything. Windows 10 provides tools that can be leveraged for both of these scenarios.

Preparing an App for Deep Linking

Let’s start with the example of a product inventory app that can display details about products. Let’s also bring a sales app into the mix that can display broad trends about what’s selling in which locations and what sort of sales needs to go where. The sales app has a drill-down UX that lets a user see details about individual products. Of course, the most detailed view of a product is in the inventory app. Figure 1 shows an illustration of the scenario about which I’m talking.

Sales App Deep Links into the Inventory App
Figure 1 Sales App Deep Links into the Inventory App

In this scenario, the first thing you need to do is make the inventory app available for launch. To do this you add a protocol declaration to the inventory app’s package manifest (package.appx­manifest). The protocol declaration is the inventory app’s way of telling the world that it’s available to be launched by other apps. Figure 2 shows what this declaration looks like. Note that I use the protocol name com.contoso.showproduct. This is a good naming convention for custom protocols because Contoso owns the domain contoso.com. The odds of some other app developer mistakenly using the same custom scheme are remote.

The Protocol Declaration
Figure 2 The Protocol Declaration

Here’s the XML the protocol declaration generated:

<uap:Extension Category="windows.protocol">
  <uap:Protocol Name="com.contoso.showproduct" />
</uap:Extension>

Next, you’ll need to add some activation code so the inventory app can respond appropriately when it’s launched using the new protocol. The code should go into the inventory app’s Application class (App.xaml.cs) because that’s where all activations are routed. You override the OnActivated method of the Application class to respond to protocol activations. Figure 3 shows what that code looks like.

Figure 3 Handling a Deep Link

protected override void OnActivated(IActivatedEventArgs args)
{
  Frame rootFrame = CreateRootFrame();
  if (args.Kind == ActivationKind.Protocol)
  {
    var protocolArgs = args as ProtocolActivatedEventArgs;
    rootFrame.Navigate(typeof(ProtocolActivationPage), protocolArgs.Uri);
  }
  else
  {
    rootFrame.Navigate(typeof(MainPage));
  }
  // Ensure the current window is active
  Window.Current.Activate();
}

You check the kind of incoming IActivatedEventArgs to see if this is a protocol activation. If it is, you typecast the incoming arguments as ProtocolActivatedEventArgs and send the incoming URI onto the ProductDetails page. The ProductDetails page is set up to parse a URI such as com.contoso.showproduct:Details?ProductId=3748937 and show the corresponding product’s details. At this point, the inventory app is ready to handle incoming deep links.

The last step to completing this scenario is to enable the sales app to deep link into the inventory app. This is the simplest part of the process. The sales app simply uses the Launcher.Launch­UriAsync API to deep link into the inventory app. Here’s what  that code might look like:

Uri uri = new Uri("com.contoso.showproduct:?ProductId=3748937");
await Launcher.LaunchUriAsync(uri);

Sharing Data Between Apps

There are scenarios where apps need to share data but don’t necessarily involve sending the user into another app. For example, my sample sales app can display sales by region and even drill down to specific stores. When showing this data categorized by product it would be useful to have the number of units of that product available in a store or region. The best source for this data is the inventory app, but in this case launching the inventory app would be disruptive to the UX. This is exactly the sort of scenario the AppService extension (bit.ly/1JfcVkx) was designed to handle.

The idea is simple: The inventory app provides a “service” that the sales app can invoke. The sales app uses this service to query the inventory app for data it has. The connection between the sales app and the inventory app, once established, can be held open as long as the sales app hasn’t been suspended.

Creating the Inventory App Service

Let’s look at how the inventory app creates and publishes the app service it intends to provide. App services basically are specialized background tasks. So in order to add an app service you add a Windows Runtime Component (Universal Windows) project to the Visual Studio solution that contains the inventory app. You can find Windows Runtime Component projects in the Add New Project window of Visual Studio under Visual C# | Windows | Universal. The project template is in a similar location for other languages.

Inside the new Windows Runtime Component project, you add a new class called InventoryServiceTask. App services are specialized background tasks because, as shown earlier, you want this code running in the background without showing a UI. To tell the OS that InventoryServiceTask is a background task, you simply need to implement the IBackgroundTask interface. The Run method of the IBackgroundTask interface will be the entry point for the inventory app service. In there, you take a deferral to let the OS know that the task should be kept around for as long as the client (the sales app) needs it. You also attach an event handler to the app service-specific RequestReceived event. This event handler will be invoked any time the client sends a request for this service to handle. Figure 4 shows what the code to initialize the inventory app service looks like.

Figure 4 Initializing the Inventory App Service in Run Method

namespace Contoso.Inventory.Service
{
  public sealed class InventoryServiceTask : IBackgroundTask
  {
    BackgroundTaskDeferral serviceDeferral;
    AppServiceConnection connection;
    public void Run(IBackgroundTaskInstance taskInstance)
    {
      // Take a service deferral so the service isn't terminated
      serviceDeferral = taskInstance.GetDeferral();
      taskInstance.Canceled += OnTaskCanceled;
      var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
      connection = details.AppServiceConnection;
      // Listen for incoming app service requests
      connection.RequestReceived += OnRequestReceived;
    }
  }
}

Now let’s look at the RequestReceived handler’s implementation. Again, you take a deferral as soon as a request comes in. You’ll release this deferral as soon as you’re done handling the incoming request. The currency of communication between the app service client and app service is a data structure called ValueSet. ValueSets are key/value dictionaries that can carry simple types such as integers, floating point numbers, strings and byte arrays.

Figure 5 shows how the inventory app service handles incoming requests. It inspects the incoming message for a command and then responds with the right result. In this case, you show the GetProductUnitCountForRegion command to which the service responds with the number of units of the product and the last time the data it has was updated. The service could well be getting this data from a Web service or just retrieving it from an offline cache. The nice thing here is that the client (the sales app) doesn’t need to know or care about from where the data comes.

Figure 5 Receiving Requests for the Inventory App

async void OnRequestReceived(AppServiceConnection sender,
  AppServiceRequestReceivedEventArgs args)
{
  // Get a deferral so we can use an awaitable API to respond to the message
  var messageDeferral = args.GetDeferral();
  try
  {
    var input = args.Request.Message;
    string command = input["Command"] as string;
    switch(command)
    {
      case "GetProductUnitCountForRegion":
        {
          var productId = (int)input["ProductId"];
          var regionId = (int)input["RegionId"];
          var inventoryData = GetInventoryData(productId, regionId);
          var result = new ValueSet();
          result.Add("UnitCount", inventoryData.UnitCount);
          result.Add("LastUpdated", inventoryData.LastUpdated.ToString());
          await args.Request.SendResponseAsync(result);
        }
        break;
      // Other commands
      default:
        return;
    }
  }
  finally
  {
    // Complete the message deferral so the platform knows we're done responding
    messageDeferral.Complete();
  }
}
// Handle cancellation of this app service background task gracefullyprivate void OnTaskCanceled(IBackgroundTaskInstance sender,
  BackgroundTaskCancellationReason reason)
{
  if (serviceDeferral != null)
  {
    // Complete the service deferral
    serviceDeferral.Complete();
    serviceDeferral = null;
  }
}

Also shown in Figure 5 is the implementation of the cancellation handler. It is important that the app service give up the deferral it took gracefully when cancellation is requested. Cancellation of an app service background task could happen either because the client closed the app service connection or the system ran out of resources. Either way, a graceful cancellation ensures that the cancellation isn’t seen as a crash by the platform.

Before anyone can call the inventory app service, you must publish it and give it an endpoint. First, you add a reference to the new Windows Runtime Component in the inventory app project. Next, you add an app service declaration to the inventory app project, as shown in Figure 6. The Entry point is set to the fully qualified name of the InventoryServiceTask class and Name is the name you’ll use to identify this app service endpoint. This is the same name app service clients will use to reach it.

The App Service Declaration
Figure 6 The App Service Declaration

Here’s the XML the app service declaration generated:

<uap:Extension Category="windows.appService"
  EntryPoint="Contoso.Inventory.Service.InventoryServiceTask">
  <uap:AppService Name="com.contoso.inventoryservice"/>
</uap:Extension>

Another piece of information that clients will need to communicate with the inventory app service is the package family name of the inventory app. The simplest way to get this value is to use the Windows.ApplicationModel.Package.Current.Id.FamilyName API within the inventory app. I usually just output this value to the debug window and pick it up from there.

Calling the App Service

Now that the inventory app service is in place, you can call it from the sales app. To call an app service a client can use the AppService­Connection API. An instance of the AppServiceConnection class requires the name of the app service endpoint and the package family name of the package where the service resides. Think of these two values as the address of an app service.

Figure 7 shows the code the sales app uses to connect to the app service. Notice the AppServiceConnection.AppServiceName property was set to the endpoint name declared in the inventory app’s package manifest. Also, the Package Family Name of the inventory app was plugged into the AppServiceConnection.PackageFamilyName property. Once ready, call the AppServiceConnection.OpenAsync API to open a connection. The OpenAsync API returns a status upon completion and this status is used to determine if the connection was established successfully.

Figure 7 Calling the Inventory App Service

using (var connection = new AppServiceConnection())
{
  // Set up a new app service connection
  connection.AppServiceName = "com.contoso.inventoryservice";
  connection.PackageFamilyName = "Contoso.Inventory_876gvmnfevegr";
  AppServiceConnectionStatus status = await connection.OpenAsync();
  // The new connection opened successfully
  if (status != AppServiceConnectionStatus.Success)
  {
    return;
  }
  // Set up the inputs and send a message to the service
  var inputs = new ValueSet();
  inputs.Add("Command", "GetProductUnitCountForRegion");
  inputs.Add("ProductId",productId);
  inputs.Add("RegionId", regionId);
  AppServiceResponse response = await connection.SendMessageAsync(inputs);
  // If the service responded with success display the result and walk away
  if (response.Status == AppServiceResponseStatus.Success)
  {
    var unitCount = response.Message["UnitCount"] as string;
    var lastUpdated = response.Message["LastUpdated"] as string;
    // Display values from service
  }
}

Once connected, the client sends the app service a set of values in a ValueSet using the AppServiceConnection.SendMessageAsync API. Notice that the Command property in the ValueSet is set to GetProductUnitCountForRegion. This is a command the app service understands. SendMessageAsync returns a response containing the ValueSet sent back by the app service. Parse out the UnitCount and LastUpdated values and display them. That’s it. That’s all it takes to communicate with an app service. You put the AppService­Connection in a using block. This calls the Dispose method on the AppServiceConnection as soon as the using block ends. Calling Dispose is a way for the client to say that it’s done talking to the app service and it can now be terminated.

Wait, Does Microsoft Use These APIs?

Of course, Microsoft uses these APIs. Most Microsoft apps that ship as part of Windows 10 are in fact Universal Windows Platform apps. This includes apps such as Photos, Camera, Mail, Calendar, Groove Music and the Store. The developers who wrote these apps used many of the APIs described here to implement integration scenarios. For example, ever notice the “Get music in Store” link inside the Groove Music app? When you tap or click that link the Groove Music app uses the Launcher.LaunchUriAsync API to get you to the Store app.

Another great example is the Settings app. When you go into Accounts | Your account and try to use the camera to take a new profile picture, it uses an API called Launcher.LaunchUriForResultsAsync to launch the camera app to take that picture. LaunchUriForResultsAsync is a specialized form of LaunchUriAsync described in more detail at aka.ms/launchforresults.

A large number of apps also use app services to communicate information to Cortana in real time. For example, when an app tries to install voice commands that Cortana should respond to, it’s actually calling an app service provided by Cortana.

Wrapping Up

Windows 10 comes with powerful tools to aid communication between apps running on the same device. These tools place no restriction or limits on what apps can talk to each other, or what kind of data they can exchange. That is very much by design. The intention is to let apps define their own contracts with each other and extend each other’s functionality. This also lets app developers decompose their apps into smaller, bite-sized experiences that are easier to maintain, update and consume. This is very important as users increasingly live their lives across multiple devices and use the device they think is best-suited to a task. All of these APIs are also universal, which means they work on desktops, laptops, tablets, phones and soon on Xbox, Surface Hub and HoloLens.


Arun Singh is a senior program manager on the Universal Windows Platform team. Follow him on Twitter: @aruntalkstech or read his blog at aruntalkstech.com.

Thanks to the following technical experts for reviewing this article: Hector Barbera, Jill Bender, Howard Kapustein, Abdul Hadi Sheikh, Stefan Wick and Jon Wiswall
Stefan Wick is a program manager and works on app execution and deployment at Microsoft
Hector Barbera is a program manager who works on app backup and roaming at Microsoft
Howard Kapustein is an engineer and works on app state management at Microsoft
Abdul Hadi Sheikh is an engineer and works on app execution at Microsoft
Jill Bender is a program manager and works on app integration scenarios as Microsoft
Jon Wiswall is an engineer and works on app model APIs at Microsoft