Dynamically detecting features with API contracts (10 by 10)

Brent Rector, a principal program manager on Windows helped write this post.

The Universal Windows Platform (UWP) allows you to write your app once and target multiple device families, while also taking advantage of new APIs introduced on later versions of the OS as well as using unique APIs only present on certain device families. Apps that adapt their behavior for different devices or OS versions are called apps are called “adaptive apps”. There are three possible adaptive dimensions for most apps:

Most adaptive apps will likely be version adaptive. For example, you may want your app to use some newer APIs that are only present on devices running different versions the UWP, while continuing to support customers who haven’t upgraded yet.

Some adaptive apps will want to be platform adaptive. Again, your app may be built for all device families, but you may want to use some mobile-specific APIs when it’s running on a mobile device. And similarly for other device families, such as IoT, HoloLens, Xbox, etc.

While this article will describe the ways in which your app can become version or platform adaptive, with Windows 10, 85% of UWP APIs are fully accessible to any app, independent of where it runs. This Universal Windows API set makes up 96.2% of the APIs used by the top 1000 apps. You can take advantage of the specialized APIs on each device to further tailor your app.

Detect features, not OS or devices

The fundamental idea behind adaptive apps is that your app checks for the functionality (or feature) it needs, and only uses it when available. The traditional way of doing this is by checking the OS version and then use those API. With Windows 10, your app can check at runtime, whether a class, method, property, event or API contract is supported by the current operating system. If so, the app can then call the appropriate API. The ApiInformation class located in the Windows.Foundation.Metadata namespace contains several static methods (like IsApiContractPresent, IsEventPresent, and IsMethodPresent) which are used to query for APIs. Here’s an example:

Note the ease of checking for the presence of a type on the current operating system using the IsTypePresent API. Formerly, such a check might have required LoadLibrary, GetProcAddress, QueryInterface, Reflection, use of the ‘dynamic’ keyword, and others, depending on language and framework.

Also note the static reference when making the method call. When using Reflection and/or ‘dynamic’, you lose static compile-time diagnostics that would, for example, inform you if you misspelled the method name.

Detecting with API contracts

So what’s an API contract? At heart, an API contract is a set of APIs. A hypothetical API contract could represent a set of APIs containing two classes, five interfaces, one structure, two enums and so on. We group logically related types into an API contract. In many ways, an API contract represents a feature – a set of related APIs that together deliver some particular functionality. Every Windows Runtime API from Windows 10 onward is a member of some API contract. The documentation at https://msdn.microsoft.com/en-us/library/windows/apps/dn706135.aspx describes the variety of API contracts available. You’ll see that most of them represent a set of functionally related APIs.

But the grouping into an API contract also provides you, the developer, some additional guarantees. The most important one is that when a platform implements *any* API in an API contract, we require that platform to implement *every* API in that API contract. In other words, an API contract is an atomic unit, and testing for support of that API contract is equivalent to testing that each and every API in the set is supported.

What does this mean for your app?

It allows your app to test whether the running OS supports a particular API contract and, after determining it does, call any of the APIs in that API contract without checking each one individually.

The largest and most commonly used API contract is the Windows.Foundation.UniversalApiContract. It contains nearly all of the APIs in the Universal Windows Platform. If you wanted to see if the current OS supports the UniversalApiContract, you would write the following code:

if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract"), 1, 0)
{
// All APIs in the UniversalApiContract version 1.0 are available for use
}

Right now this is a slightly silly check. All devices that run Windows 10 support version 1.0 of the UniversalApiContract. But with the next major update of Windows 10, we may introduce additional APIs, creating a version 2.0 of the UniversalApiContract and adding those new universal APIs to it. An app that wants to run on all devices, but also wants to use new APIs introduced in version 2.0 when available, could use the following code:

if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract"), 2, 0)
{
// This device supports all APIs in UniversalApiContract version 2.0
}

Of course, if your app only needed to call one single method from version 2.0, it could simply check for the method using IsMethodPresent. Use whichever approach you find the easiest.

There are other API contracts besides the UniversalApiContract. Most of them represent a feature/set of APIs not universally present on all Windows 10 platforms (else we’d add the APIs to the UniversalAPIContract). Instead, the APIs in such API contracts are present on one or more device families, but not all of them. As mentioned previously, you no longer need to check for a particular type of device, then infer the support for an API from the device family. Simply check for the set of APIs your app wants to use.

I can now rewrite my original example to check for the presence of the Windows.Media.Playlists.PlaylistsContract instead of just checking for the present of the Playlist class:

if(ApiInformation.IsApiContractPresent("Windows.Media.Playlists.PlaylistsContract"), 1, 0)
{
// Now I can use all PlayList APIs
}

Any time your app needs to call an API that isn’t present across all device families, you need to add a reference to the appropriate Extension SDK that defines the API. In Visual Studio 2015, go to the Add Reference dialog and open Extensions tabs. Today you can find the three most important extensions there: Mobile Extension, Desktop Extension and IoT Extension.

All your app needs to do, however, is check for the presence of the desired API contract and call the appropriate APIs conditionally. There’s no need to worry about the type of device.

As it turns out, the Playlist class is (currently) only available on Desktop devices, not Mobile, Xbox, and other device families. (Though that could change in a future release!) So you need to add a reference to the Desktop Extension SDK before any of the prior code compiles.

Lucian Wischik created a tool that analyzes your code and when your code calls into a platform-specific API, the analyzer verifies that you’ve done an adaptivity check around it — if you haven’t then it reports a warning. It also provides a handy “quick-fix” to insert the correct check, by pressing Ctrl+Dot or clicking on the lightbulb. See https://github.com/ljw1004/blog/blob/master/Analyzers/PlatformSpecificAnalyzer/ReadMe.md for more details. It can also be installed via NuGet if you search for it.

Let’s wrap up by looking at some more complete examples of adaptive coding for Windows 10. First, some code that IS NOT correctly adaptive.

We are now verifying that the optional API is actually supported on this device before calling the appropriate method. Note that you’ll likely want to take this example further and never even display the UI that calls the CreatePlaylist method if your app detects that playlist functionality isn’t available on the device.

Here is another example. We’ll assume our app wants to take advantage of a Mobile device’s dedicated camera button. If I directly referenced the HardwareButtons object for the CameraPressed event while on a desktop without checking that HardwareButtons is present, my app would crash.

I know the current version of Xbox One doesn’t support picking folders. I mean
var picker = new Windows.Storage.Pickers.FolderPicker();
var pfolder = await picker.PickSingleFolderAsync();
but I can’t find a way to detect it properly. So far I can disable this feature (which is my intention) checking if the app is running on Xbox, but if in a future the feature is implemented my app won’t work due to my workaround.
How should I use these adaptive api’s for my scenario?

There is one problem that I encountered by following the post. I was dynamically detecting for UniversalApiContract v4. However, when the code was running on .NET 4.5 classic desktop app (manifested for Windows 10 Runtime), there was an exception about “Windows Runtime-type XXXXX is not found”.

Turned out that the when inlining the contract-specific call in the IF that checks for contract presence, then it will throw exception.

It needs to be coded in the following way instead:
public void DoContractSpecificStuff()
{
if(ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 4))
{
DoContractV4Call();
}
else
{ /* Do <v4 stuff*/ }
}

I think I understood why .NET was trying to load the GattCharactersiticsResult class even though the IF condition was not satisfied. The problematic code was actually placed in an ASYNC method. Due to that, the compiler was turning the async method into that tricky async nested class needed to do the dirty job of the await. By doing so it was transforming the code in such a way, that GattCharactersiticsResult field was getting loaded on method call, despite the fact that execution path was not hitting the specific GattCharactersiticsResult field at all.