One of the most exciting pronouncements at the 2014 BUILD conference was Microsoft’s introduction of “universal apps,” which run on PCs, tablets, and phones. According to Microsoft, universal apps will one day run on Xboxes, too. The universal app represents the first step in a convergence that Microsoft has been seeking for a long time – a convergence of platforms and APIs that allows you to write an application one time and have it run on a variety of Windows devices and form factors.

From a consumer standpoint, a universal app is one that has a single app identity across all devices. Imagine that you buy it once (assuming it’s not free) from the Windows Store, and then download it to your Windows tablet and your Windows phone. Once there, the app offers an optimized experience for each form factor, shares data across devices through the cloud, supports in-app purchases, and more. A consumer could care less whether the same binary is being installed on each device; all he or she knows is that the same app works on a PC, a tablet, or a phone.

From a developer’s perspective, a universal app is not what you might think. It’s not a single binary that runs on multiple platforms. Rather, it takes the form of a Visual Studio solution containing multiple projects: one project for each targeted platform, plus a project containing code and resources shared between platforms. Because Windows Phone 8.1 implements the vast majority of the WinRT APIs that Windows 8.1 implements, a LOT of code can be shared between a Windows project and a Windows Phone project. Most of the platform-specific code you write is UI-related, which, as a developer, I’m perfectly fine with because a UI that looks great on a 30” monitor must be tweaked to look equally great on a 5” phone, and vice versa. Even there, Microsoft has done a lot of work to bridge the gap. For example, Windows Phone 8.1 includes the same Hub control featured in Windows 8.1, meaning you can use similar markup on both platforms to produce a UI tailored to each form factor.

You can download the RC release of Visual Studio 2013 Update 2 and see first-hand what’s involved in building a universal app. I did, and to help developers get acquainted with universal apps, I built a XAML and C# version of the Contoso Cookbook app that I originally wrote for Microsoft to help introduce developers to WinRT and Windows Store apps. The screen shot below shows Contoso Cookbook running side by side on a Windows tablet and a Windows phone. What’s remarkable is how little platform-specific code I had to write, and how similar the XAML markup is for both platforms.

Contoso Cookbook Universal

You can download a zip file containing the Visual Studio solution for Contoso Cookbook. Or you can follow along as I highlight some of the more interesting aspects of the development process. Either way, I hope you come away excited about universal apps and the prospects that they hold for the future of Windows development. The future is now, and the future has “universal apps” written all over it.

Getting Started (and Learning to Share)

The first step in implementing Contoso Cookbook as a universal app was to use Visual Studio 2013 Update 2’s New Project command to create a solution. From the list of project types available, I selected “Hub App (Universal Apps)” to create basic 3-page navigation projects anchored by Hub controls:

image

The result was a solution containing three projects: a Windows 8.1 project named ContosoCookbookUniversal.Windows, a Windows Phone 8.1 project named ContosoCookbookUniversal.WindowsPhone, and a third project named ContosoCookbookUniversal.Shared. Here’s how it looked in Solution Explorer after I added a few  more files to the Windows and Windows Phone projects representing flyouts and pages that are specific to each platform:

image

The Shared project doesn’t target any specific platform, but instead contains resources that are shared between the other two projects via shared (linked) files. In an excellent blog post, my friend and colleague Laurent Bugnion documented some of the ins and outs of working with the Shared project. It can include source code files, image assets, and other files that are common to the other projects. Source code files placed in the Shared project must be able to compile in the other projects – in this case, they must be able to compile in a Windows 8.1 project and a Windows Phone 8.1 project. You can’t add references in the Shared project, but you can add references in the other projects and use those references in the Shared project. Those references can refer to platform-specific assemblies, portable class libraries (PCLs), and even Windows Runtime components. What’s really cool is that if you add references to platform-specific assemblies to the Windows project and the Windows Phone project and the assemblies expose matching APIs, you can call those APIs from source code files in the Shared project.

You can even use #if directives to include platform-specific code in a shared file. In the case of Contoso Cookbook, I needed to add some Windows-specific code to App.xaml.cs to add commands to the system’s Settings pane – something that doesn’t exist in the Windows Phone operating system. So in App.xaml.cs, I added the following conditional using directive:

#if WINDOWS_APP
    using Windows.UI.ApplicationSettings;
#endif

And then, in the OnLaunched override, I added this:

#if WINDOWS_APP
// Add commands to the settings pane
SettingsPane.GetForCurrentView().CommandsRequested += (s, args) =>
{
    // Add an About command to the settings pane
    var about = new SettingsCommand("about", "About", (handler) =>
        new AboutSettingsFlyout().Show());
    args.Request.ApplicationCommands.Add(about);

    // Add a Preferences command to the settings pane
    var preferences = new SettingsCommand("preferences", "Preferences", (handler) =>
        new PreferencesSettingsFlyout().Show());
    args.Request.ApplicationCommands.Add(preferences);
};
#endif

The result was that the code compiled just fine and executed as expected in the Windows app, but effectively doesn’t exist in the Windows Phone app. You can prove it by running the Windows version of Contoso Cookbook, selecting the Settings charm, and verifying that the Settings flyout contains the About and Preferences commands registered in App.xaml.cs:

image

I added other files to the Shared project so they could be used in the other projects. For example, I added CS and JSON files to the DataModel folder in the Shared project to represent the data source shared by the Windows app and the Windows Phone app, and I added CS files to the Common folder representing value converters and helper components used in both projects. I even added an Images folder containing images used by both apps. Here’s what the Shared project looked like at the completion of the project:

image

Again, thanks to file linking, you can use anything in the Shared project from the other projects as if it were part of those projects. This provides a common base to build from, and to the extent that you can build what each project needs into the Shared project, you can minimize the amount of platform-specific code and resources required.

Building the UIs

Most of the UI work takes place in the platform-specific projects, allowing you to craft UIs that look great on PC, tablets, and phones alike, but that share common data, resources, components, and even view-models.

Even though the UI for the Windows version of Contoso Cookbook is defined separately from the UI for the Windows Phone version, they have a lot in common that reduced the amount of work required to build them. Each project, for example, contains a start page named HubPage.xaml that uses a Hub control to present content to the user. I used different data templates to fine-tune the UIs for each platform, but the basic structure of the XAML was the same in both projects.

Another UI component ported from Windows 8.1 to Windows Phone 8.1 is the CommandBar class. To implement command bars in both projects, I simply copied the following XAML from ItemPage.xaml in the Windows project to ItemPage.xaml in the Windows Phone project:

<Page.BottomAppBar>
    <CommandBar>
        <AppBarButton Icon="ReShare" Label="Share" Click="OnShareButtonClicked" />
    </CommandBar>
</Page.BottomAppBar>

The click handler calls DataTransferManager.ShowShareUI to display the system’s Sharing pane. (In Windows, you can show the Sharing pane programmatically, or you can rely on the user to show it by tapping the Share charm in the charms bar. There is no charms bar in Windows Phone, so if you wish to share content, you must present the system’s Sharing page programmatically.) Why did I use Click events rather than commanding? Because I couldn’t get commanding on AppBarButtons to work reliably. I assume this is a consequence of the fact that we’re working with tools and platforms that aren’t quite finished yet, and that commanding will work as expected in the final releases.

Both projects use DataTransferManager.DataRequested events to share recipe images and text from the items page. The code to share content is identical on both platforms, so after registering a handler for DataRequested in each project’s ItemPage.xaml.cs, I factored the sharing code out into a static method in the ShareManager class I added to the Shared project, and called that method from each project’s DataRequested event handler. Here’s the relevant code in ShareManager:

public static void ShareRecipe(DataRequest request, RecipeDataItem item)
{
    request.Data.Properties.Title = item.Title;
    request.Data.Properties.Description = "Recipe ingredients and directions";

    // Share recipe text
    var recipe = "rnINGREDIENTSrn";
    recipe += String.Join("rn", item.Ingredients);
    recipe += ("rnrnDIRECTIONSrn" + item.Directions);
    request.Data.SetText(recipe);

    // Share recipe image
    var reference = RandomAccessStreamReference.CreateFromUri(new Uri(item.ImagePath));
    request.Data.Properties.Thumbnail = reference;
    request.Data.SetBitmap(reference);
}

In cases where UI capabilities varied significantly between platforms, I wrote platform-specific code. For example, as noted earlier, since Windows Phone doesn’t have a charms bar, I used #if to include Windows-specific code in the shared App.xaml.cs file to hook into the charms bar. Along those same lines, I added settings flyouts based on Windows’ SettingsFlyout class to the Windows project in files named AboutSettingsFlyout.xaml and PreferencesSettingsFlyout.xaml. Since SettingsFlyout wasn’t ported to the Jupiter (XAML) run-time in Windows Phone 8.1, I added a settings page named SettingsPage.xaml to the Windows Phone project. The screen shot below shows the Preferences flyout in the Windows app and the Settings page in the Windows Phone app side by side:

image

In each case, the user is presented with a UI that allows him or her to choose to load data locally from in-package resources or remotely from Azure. The code that loads the data and parses the JSON is found in the shared RecipeDataSource class and works identically on both platforms. (Fortunately, the Windows.Data.Json and Windows.Web.Http namespaces are present in WinRT on the phone and in Windows, so the code just works in both places.) And in each case, I built the settings UI around the ToggleSwitch control that’s present on both platforms. Same concept, different implementation, and a perfect example of how platform-specific projects retain the ability to use platform-specific APIs and controls without impacting the other projects.

I didn’t include search functionality in the apps even though it was present in the original Contoso Cookbook. I will probably add it later, but the reason I chose not to for now is that while Windows has a SearchBox control, Windows Phone does not. That means I’ll need to build my own search UI for the phone – not a big deal, really, since I can easily put the search logic in a component that’s shared by both projects.

The Bottom Line

Most of the code that drives the Windows app and the Windows Phone app is shared, and while the UIs are separate, they’re similar enough that building both was less work than building two UIs from scratch. If I had built a Windows Phone version of Contoso Cookbook for Windows Phone 7 or 8, it would have been a LOT more work since Windows Phone 7 contained no WinRT APIs and Windows Phone 8 contained only a small subset.

If you’re interested, I’ll be delivering a session on universal apps at the Software Design & Development conference in London next month. It should be a fun time for all as we delve into what universal apps are and how they’re structured. I’ll have plenty of samples to share as we party on universal apps and learns the ins and outs of writing apps that target separate but similar run-times.

Microsoft has talked a lot about “convergence” in recent months, and now we see evidence of what it means: one API – the WinRT API – for multiple platforms, and a high degree of fidelity between UI elements for each platform that doesn’t preclude developers from using platform-specific elements to present the best possible experience on every device. This is the future of Windows development. And for many of us, it couldn’t have come a moment too soon.

  • Very useful post. Thanks a lot.

  • Extremely helpful content and a sample on top 🙂 Thanks Jeff for posting this article. Currently we are integrating our web-app with a universal app and your article helped us to get a quick overview of what is possible and how to share resources between Windows 8.1 and Windows Phone 8.1 apps. We will check the options as well, on how and what kind of XAML resources we are going to share between both app-types. Thanks to Laurent we are also able to use MVVM-Light for both apps and share view-models as well.

  • Pingback: Building Contoso Cookbook with Xamarin Forms – Wintellect DevCenter()

  • Netas Smith

    where are the images?

  • Netas Smith

    where are the images?

    • Rachel Snowbeck

      Approved