Last year, I wrote a universal app named Pic Me (later renamed to “Picture Me”) for Windows 8.1 and Windows Phone 8.1. The main reason I wrote it was that my daughter said “wouldn’t it be cool to have an app that shows you all the photos you’ve been tagged in on Facebook, and that lets you download those photos, too?” But the other reason I wrote it was that it seemed like a great opportunity to sink my teeth into a universal app for which there was a genuine need – not to mention the fact that it was a chance to use WebAuthenticationBroker and a handful of other WinRT classes that I hadn’t used much in the past.
Boy, was it an adventure. Because the app used WebAuthenticationBroker, FileSavePicker, and FolderPicker, and because the continuation model used by those classes was very different on Windows and Windows Phone, I ended up writing a fair amount of platform-specific code. Roughly one third of the code was shared, one third was Windows-specific, and one third was Windows Phone-specific. It was clear that the APIs were converging, but it was also clear that they had a long way to go if you wanted one binary to run on a variety of devices.
So it was with some skepticism that I set out to turn Picture Me into a Universal Windows Platform (UWP) app for Windows 10. How much had the APIs converged? To what extent would I be able to use WebAuthenticationBroker, FileSavePicker, and FolderPicker without writing reams of platform-specific code? And how easy would it be to recast the UI to be consistent with the apps that come with Windows 10? I have version 0.9 ready to go now, and let me just say that I was pleasantly surprised. The Universal Windows Platform delivers on its promise of one API for all devices, while still allowing you to leverage unique features of individual devices. I think this portends well for the Windows platform, and I hope other developers are as excited as I am about its potential.
Here’s the fruit of my efforts so far, complete with sample code for developers interested in learning about UWP.
Introducing Picture Me for Windows 10
When you launch Picture Me on a desktop PC, you’ll be greeted with a WebAuthenticationBroker inviting you to type in your Facebook credentials. (In the current build of Windows 10 – the one released during BUILD 2015 – WebAuthenticationBroker might be positioned outside the app’s window. Don’t worry about that; I’m sure that will be fixed before RTM.) Type in a user name and password, and in a few moments you’ll see a list of all the photos you’re tagged in on Facebook, grouped by year:
Clicking a photo navigates to a page that shows a photo detail:
You can click the Back button in the command bar at the top of the page to return to the main page, or the Save button to save a copy of the photo on your PC. Or you can click the Info button (the question mark) to display metadata about the photo, including when it was uploaded to Facebook and by whom, and the caption (if any) written by the person who uploaded it:
The Info button is a toggle-button, so clicking it a second time hides the partially transparent overlay containing the photo metadata.
You don’t have to download photos one at a time. On the main page, you can click the Select button (highlighted in blue below) to toggle selection mode on and off. In selection mode, you can select all the photos you wish to download (or click the Select All button to its right to select them all), and then click the Save button to download everything you selected:
The thumbnails displayed on the main page are low-res versions selected for speedy downloads, but the photos downloaded to your PC are the highest-res versions Facebook stores.
But what about the phone? A UWP version of Picture Me should run on Windows Phones, too, right? Here’s how the same app – the same APPX package, no less – looks on a Windows phone:
Functionally, the app is exactly the same on the phone. You can view photo details, download photos individually or as a group, etc. The only platform-specific code in the entire app is code that responds to clicks of the phone’s hardware Back button. And that code is qualified at run-time using API metadata so that it doesn’t crash on non-phone devices. Intrigued? Then let’s take a look behind the scenes to see what true API convergence looks like.
One APPX to Rule Them All
If you open the Picture Me solution in Visual Studio 2015 (I used the RC version that was released during BUILD), the first thing you’ll notice is that the solution contains just one project. Unlike universal apps in Windows 8, which typically included one shared project plus additional projects for various devices and produced separate binaries for each device, this one produces a single APPX that runs on all UWP devices:
The project contains two pages: MainPage.xaml, which represents the main page, and PhotoPage.xaml, which represents the photo-detail page. MainPage.xaml contains a GridView control that’s bound to a grouped collection of FacebookPhoto objects created from JSON data retrieved from Facebook. It also includes a pair of AdaptiveTriggers that set the padding around the GridView based on the screen/window width:
<VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="WindowStates"> <VisualState x:Name="WideState"> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="520" /> </VisualState.StateTriggers> <VisualState.Setters> <Setter Target="Photos.Padding" Value="24,0,24,8" /> </VisualState.Setters> </VisualState> <VisualState x:Name="NarrowState"> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="0" /> </VisualState.StateTriggers> <VisualState.Setters> <Setter Target="Photos.Padding" Value="8" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups>
I wrote about AdaptiveTrigger in a previous article, so I won’t belabor the point here. The upshot is that all the crappy platform-specific code I had to write to get WebAuthenticationBroker, FileSavePicker, and FolderPicker to work on Windows Phone 8.1 was no longer required. I basically copied the broker and picker code from the Windows 8.1 version of the app, pasted them into the new version, and let UWP do the rest.
Handling the Phone’s Back Button
When you write a Universal Windows Platform app, you don’t target a specific operating-system version. Instead, you target one or more API contracts. By default, your can code use any APIs that belong to the Universal Windows Platform, which is a set of API contracts that are implemented on ALL Windows devices. That API set is a rich and diverse one containing tens of thousands of types and type members.
UWP doesn’t contain any APIs for dealing with the hardware buttons on a phone, or any other APIs that are specific to a device family. Yet apps such as Picture Me MUST be able to respond to clicks of the hardware Back button on a phone. Otherwise, clicking that button while resting on the photo page (PhotoPage.xaml) will exit the app rather than navigate back to the main page (MainPage.xaml).
The solution comes in the form of device-family extension SDKs. There are currently two of them: one for desktop devices, and another for mobile devices. (Expect more extension SDKs to be available by RTM.) Let’s say you’re writing a UWP app and you want it to be able to print when it’s running on a desktop PC. There are no printing APIs in UWP, but there are in the Microsoft Desktop Extension SDK. So you add a reference to that SDK to your project using Visual Studio’s Add Reference command, and then call desktop printing APIs as needed. Similarly, you can add a reference to the Microsoft Mobile Extension SDK to target APIs for the hardware buttons on phones, for smart cards, and more.
What happens if an app that uses APIs in extension SDKs runs on a device that doesn’t support those extensions? It crashes – unless you take steps to make sure the unsupported APIs aren’t called. Microsoft calls this adaptive coding, and UWP provides the APIs you need to write adaptive code. Chief among those APIs is a static class named Windows.Foundation.Metadata.ApiInformation. With it, you can determine at run-time whether the device your app is running on supports a specific type, type member, or API contract.
In Picture Me, I needed to be able to register a handler for the BackPressed events fired when the user presses the Back button on a Windows phone. I first added a reference to the Microsoft Mobile Extension SDK:
I then added the following code to PhotoPage.xaml.cs:
// If a hardware Back button is present, hide the "soft" Back button // in the command bar, and register a handler for presses of the hardware // Back button if (Windows.Foundation.Metadata.ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons")) { BackButton.Visibility = Visibility.Collapsed; Windows.Phone.UI.Input.HardwareButtons.BackPressed += OnHardwareBackButtonPressed; }
The call to ApiInformation.IsTypePresent returns true on a phone (technically, on any device that belongs to the “mobile” device family) and false on any other Windows device. If it returns true, I hide the software Back button in the command bar (you’ll need that button in a desktop app, because without it, you can’t navigate back to the main page) and register a handler for BackPressed events fired by the hardware Back button.
Incidentally, there IS a way to respond to the phone’s Back button without writing device-family-specific code or resorting to extension SDKs. If you register a handler for Windows.UI.Core.SystemNavigationManager.BackRequested events, your handler will be called when the Back button is pressed. It doesn’t seem to be wholly reliable right now, but I suspect that’s more an emulator problem than a Windows problem. Soon, SystemNavigationManager is going to provide a software back button for apps that need it. When it does, I’ll revise Picture Me to take advantage of it.
Summary
Microsoft has been working for a long time to achieve 100% API convergence on Windows devices. Gone are the days of writing to one API for a desktop app and another API for a phone app. With Windows 10 and the Universal Windows Platform, it has never been easier to write one app that runs on so many different devices. I can’t wait to see Picture Me running on the Xbox in my living room. It won’t be long until I do, because I already have the APPX I need.