September 2014

Volume 29 Number 9

Modern Apps : Build Universal Apps for the Windows Platform

Rachel Appel | September 2014

Rachel AppelIf you develop for the Windows platform, you can now write apps that target both Windows 8.1 and Windows Phone 8.1 with a single, shared codebase by creating a Universal App. Universal Apps are new to Windows. They let you share significant portions of your JavaScript, C#/Visual Basic, and C++ code across the Windows ecosystem.

At the Build 2014 conference, Microsoft stated it will extend the Universal App concept to Xbox in the future (bit.ly/1p19O7O). You can do it now, but you’d need Visual Studio 2012 and a few workarounds. This is particularly exciting news for those who build games and media apps. You now have a head start building for the various Windows platforms, and adding an Xbox project later. Here, I’ll stick to Windows and Windows Phone.

Outside the Windows and Xbox platforms, you can use Universal Apps to write cross-platform apps with HTML and JavaScript. You can write strictly HTML5. You can eschew the Windows Library for JavaScript (WinJS) for iOS and Android. You can also use WinJS just for native features in the Windows ecosystem. If you’re using XAML with C#, you can use a tool like Xamarin to publish across the major platforms.

Get Started with Universal Apps

You can build Universal Apps using Visual Studio 2013 Update 2 on Windows 8.1 using project templates for JavaScript, C#/Visual Basic and C++. This means you can continue to use your favorite language to build apps in this latest Windows update.

The structure of the new Universal App is a Visual Studio solution with at least three projects—Windows, Windows Phone and Shared. Coincidentally, there are three project types of Universal Apps from which to choose—the Blank, Hub and Navigation templates. You can use the New Project dialog in Visual Studio to create a Universal App with any of these three new templates.

Because Visual Studio creates a solution containing three projects, you would rightfully expect the Shared project is where the shared code goes. Put as much code here as possible. Code sharing and reuse has many benefits, such as easier maintenance and less-expensive bug fixes. Of course, you can have too much of a good thing. If you try to share everything, you’ll end up with too many branching constructs in your code, such as if and switch statements that will become unruly. As a rule of thumb, if you find yourself copying and pasting code to use in multiple places, you should separate the code into the separate projects. This concept is called DRY—short for Don’t Repeat Yourself. The DRYer the code is, the better.

A Shared project is a project file with a .shproj extension. Shared projects behave like regular projects, except they don’t create compiled output or a package. They’re simply a way to make code available for two or more projects without having to reference or copy and paste. You can share code like this because in Windows 8.1, the Windows Runtime extends across both OSes. Some API members aren’t compatible between platforms, but calls to those can go in their corresponding projects.

Figure 1 shows the architecture for a simple Universal App. This app, called Countdown, displays the number of days to an event date the user enters alongside number of days until the event.

Side-by-Side Views of Windows 8.1, Windows Phone 8.1 and a Shared Project
Figure 1 Side-by-Side Views of Windows 8.1, Windows Phone 8.1 and a Shared Project

As you can see in Figure 1, each project contains its own UI code. The Shared project contains a few JavaScript files, as well as a shared home page.

Sharing Code Across Projects

Each platform project has a default.html and .js file that acts as the app starting point. These reference the corresponding platform-specific JavaScript files. For this example, you don’t need any special code for lifecycle management. If you do, though, you might need to separate those files depending on how OS-specific your needs are for the app.

These are the Windows default.html references:

<script src="//Microsoft.WinJS.2.0/js/base.js"></script>
<script src="//Microsoft.WinJS.2.0/js/ui.js"></script>

These are the Windows Phone default.html references:

<script src="//Microsoft.Phone.WinJS.2.1/js/base.js"></script>
<script src="//Microsoft.Phone.WinJS.2.1/js/ui.js"></script>

These are references to the WinJS 2.0 for Windows and the WinJS 2.1 for Windows Phone. The latter contains the JavaScript that takes advantage of any differences on Windows Phone.

In the sample Countdown app, there’s a page directory in each platform project. Each directory contains subdirectories for each page. The subdirectories include the page itself and related items such as CSS or JS files. In Figure 1, you can see the pages are home, addEvent and privacy. These folders merge between the platform and shared projects at run time. You may only have one file with a unique name in each folder. For example, you can save the home.html page in the /pages/home/ directory, but only in one project. In this case, I’ve put it in the Shared project folder.

The home page is the main part of the app where the countdown events display. Figure 2 shows the shared UI code for home.html. Notice the corresponding home.css files are located in their respective projects. This means if you use the same CSS class names across projects, you can use the same HTML. But these would be restyled with the varying CSS for each platform, as you can see in Figure 2. You can have one base of HTML and style with differing CSS for each platform project.

Figure 2 The HTML and CSS that Create the Home Page for Both Projects

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>Countdown</title>
  <link href="/pages/home/home.css" rel="stylesheet" />
  <script src="/js/MobileServices.js"></script>
  <script src="/js/data.js"></script>
  <script src="/pages/home/home.js"></script>   
</head>
<body>
  <div id="contenthost">
    <h1 id="title" class="title">Countdown from</h1>
    <div id="maincontent">
      <div id="listViewTemplate" data-win-control="WinJS.Binding.Template"
        class="win-container">
        <div data-win-bind="style.background: color" class="win-item">
          <h1 class="h1" data-win-bind="textContent: daysToGo"></h1>
          <h2 class="h2"> days to go until</h2>
          <h2 class="h2" data-win-bind="textContent: eventTitle"></h2>
          <h2 class="h2" data-win-bind="textContent: eventDate"></h2>
        </div>
      </div>
      <div id="listView" data-win-control="WinJS.UI.ListView" 
           class="win-listview"
           data-win-options="{ selectionMode: 'single' }">
      </div>
    </div>
    <div id="addEventHTMLControl">
    </div>
  </div>
</body>
</html>
<!-- CSS for the Windows  ListView -->
#listView {
height:500px;
}
#listView .win-viewport {
  height:415px;
}
#listView .win-item {   
  width:300px;
  height:185px;
  padding: 0 0 0 5px;
}
.h1 {
  font-size:1.5em;     
}
.h2 {
  font-size:1em;       
}
<!-- CSS for the Windows.Phone ListView -->
#listView .win-item {   
  width:100%;
  height:125px;
  padding: 5px 0 0 5px;
}
.h1 {
  font-size:2em;     
}
.h2 {
  font-size:1.5em;       
}

Figure 3 converts the <div id="addEventHTMLControl"> element from Figure 2 into a WinJS.UI.HtmlControl on Windows. This means you can store the add event code in the addEvent.html file.

Figure 3 Part of the Home.js Ready Function That Determines the OS and Builds the UI

var listView = document.querySelector("#listView").winControl;
var packageName = Windows.ApplicationModel.Package.current.id.name;
if (packageName === "Countdown.WindowsPhone") {
  document.querySelector("#privacyButton").addEventListener("click",
    this.navigateToPrivacyPage);
  document.querySelector("#addButton").addEventListener("click",
    this.navigateToaddEventPage);
  listView.layout = new WinJS.UI.ListLayout();
}
else if (packageName === "Countdown.Windows") {
  document.querySelector("#addButton").addEventListener("click",
    this.showAddEventFlyout);
  var htmlControl =
    new WinJS.UI.HtmlControl(document.querySelector("#addEventHTMLControl"),
    { uri: '/pages/addEvent/addEvent.html' });
  listView.layout = new WinJS.UI.GridLayout();
}

Within the shared home.js, there are a few points where you need to write some conditional code. This determines which platform you’re using and acts accordingly. Check for the package name to determine which platform is running. The package name is in the package.appmanifest under the Packaging tab. By default, this is a GUID. You can change it to something friendly to use in code. Once you’ve done that, you can query it to determine the current platform at run time. Figure 3 shows a sample of what this code looks like when determining which events to wire up and which UI component to use.

As you can see, Figure 3 wires the appBar buttons to features that are distinct to each app type. This could be a Flyout versus an entire page for both adding events and viewing privacy settings. It’s easy in Windows. The privacy page is part of the app settings, launched from the Settings charm. The code that sets up Charms usually goes in default.js on activated event. Because Windows Phone doesn’t have the notion of Charms, use the appBar as a way to navigate to the privacy page, as reflected in Figure 3.

The best way to add events is to use a Flyout in Windows. Then, due to the differing form factors, create a complete HTML page on Windows Phone. Popups and dialogs just don’t do well on phone-sized devices. The addEvent Flyout or page is where the user enters new event details. The HTML for the addEvent Flyout is also in Figure 3.

Also notice in Figure 3 the ListView layout changes between a grid and list layout in the code, depending on which platform is detected. The ListLayout property is handy, because you need just one block of HTML from Figure 2. Combining this with one shared chunk of code from Figure 3 does the legwork to switch between layouts.

In the js folder, you’ll find the same data.js and navigator.js files as their counterparts in any given solution based on the Navigation template. Apps on both platforms need the same data and navigation scheme, so these files can stay in the Shared project. The following code is the definition for the Data namespace, which is familiar to WinJS developers:

var list = new WinJS.Binding.List();
WinJS.Namespace.define("Data", {
  loadEventData: loadEventData,
  saveEventItem: saveEventItem,
  deleteEventItem: deleteEventItem,
  items: list,     
});

Both platform projects consume the data the exact same way, by JSON data put into a WinJS.Binding.List object. The only difference is how they style the data.

When developing Universal Apps, you should write unit tests and make changes in the shared codebase. This means at some point, you’ll need to run the project to verify the results. When debugging, choose the project you want to debug, right-click on that project in the Solution Explorer and choose Set as Start Project.

This will make the project appear bold in Solution Explorer. When you click Run, press F5, or launch the project, then the Start Project is the one that runs. You can switch between projects as the starting project as needed.

The Shared project can’t be the Start Project. When you run a specific project, Visual Studio packages, deploys and loads that project’s assets, as well as the ones from the Shared project. Then it starts the corresponding emulator if it isn’t already and you’re good to go. To learn more about debugging Windows Store apps, see my blog post at bit.ly/1hTpxHB.

Create Native UIs

The UI will share the least amount of code. Various form factors dictate both the amount and presentation of data. Present data with HTML and make it look good with CSS. Dealing with different HTML and CSS is simpler if you split it into separate projects, including the CSS Media Queries for each range of devices per platform.

In the case of the Countdown app, you want a grid display on Windows and a vertical list on phones. Do this by placing the home.html file in the Shared project, and the home.css files in separate projects. The addEvent and privacy pages go in their corresponding platform projects. To see this folder structure, look back at Figure 1.

In the Countdown.Windows app, the user adds an event by tapping or clicking the Add appBar button. This displays a popup dialog using a Flyout control. In Countdown.WindowsPhone, the app takes the user to an entire addEvent page. Here’s the code that designs the Flyout in Countdown.Windows:

<div id="eventFlyoutPanel" data-win-control="WinJS.UI.Flyout">
  <table width="100%" height="100%">
    <tr><td>Date:</td><td><span id="eventDate"
      data-win-control="WinJS.UI.DatePicker"></span></td></tr>
    <tr><td>Event:</td><td><input type="text" id="eventTitle" /></td></tr>
    <tr><td>&nbsp;</td><td><input type="button" id="confirmButton"
      value="Submit" /></td></tr>
  </table>
</div>

It’s a small table containing a DatePicker and TextBox. Neither Flyouts nor DatePickers run on Windows Phone. This Flyout is configured in home.html as a WinJS.UI.HtmlControl. Figure 4 gives you a look at the runtime appearance of the screen this code creates.

The Runtime Appearance of the Countdown App Flyout
Figure 4 The Runtime Appearance of the Countdown App Flyout

The Countdown.WindowsPhone project is much different. Instead of a Flyout, you must navigate to the addEvent page. Having no DatePicker is no problem. You can collect the event’s date with the proven mobile technique of creating three <select> elements, one for each part of the date (Day/Month/Year). Figure 5 contains a sample of the Countdown.Windows addEvent HTML code and Figure 6 shows what it will look like at run time on Windows Phone.

Figure 5 A Sample of the Countdown.Windows addEvent HTML Code

<h1 id="title" class="title">
  Add event
  </h1> 
  <div>
  Month
    <select id="eventMonth">
    <option value="--Select--">--Select--</option>
    <option value="01">January</option>
    <option value="02">February</option>
    <option value="03">March</option>
    <option value="04">April</option>
    <option value="05">May</option>
    <!-- options 6-11, cut for brevity -->
    <option value="12">December</option>
  </select>
  Day
  <select name="day" id="day">
    <option value="--Select--">--Select--</option>
    <option value="01">01</option>
    <option value="02">02</option>
    <option value="03">03</option>
    <option value="04">04</option>
    <option value="05">05</option>
    <!-- options 6-30, cut for brevity -->
    <option value="31">31</option>
  </select>
  Year<select id="eventYear">
    <option value="--Select--">--Select--</option>
    <!-- all other options created via client script -->
    </select>
  </div>
  <div>Event<input type="text" id="eventTitle" /></div>
  <div>
    <input type="button" id="backButton" value="Back" />
    <input type="button" id="confirmButton" value="Submit" />
  </div>
<script type="text/javascript">
  (function () {
    var yearSelect = document.querySelector("#eventYear");
    for (var i = 2014; i < 2075; i++) {
      yearSelect.options[i] = new Option(i, i);
    }
  })();
</script>

You need to ensure the controls you want to use are available across OSes. Hub, Flyout, DatePicker and TimePicker controls aren’t available on Windows Phone. The Pivot control isn’t available on Windows. Fortunately, if you’re using the popular Hub or Pivot controls, they’re usually interchangeable. So you can use Hub on Windows and Pivot on the phone and get the same things done. Simple dropdown controls work for capturing dates. Figure 6 shows what this will look like on the Windows phone.

The Countdown and Add Event Page as Generated on a Windows Phone
Figure 6 The Countdown and Add Event Page as Generated on a Windows Phone

Review your code and separate code targeting OS-specific tasks. The more of these types of tasks, the more it should be separated into its own project. If there are only a few blocks, then it’s OK to leave it in the Shared project.

More Ways to Share Code

You can apply architectural patterns as a way to organize, maintain and share code, such as Model-View-ViewModel (MVVM) or Model-View-Controller (MVC). In the case of the Countdown app, sharing code in the Shared project works fine.

Another exciting announcement from the Build conference is that the Windows Runtime now supports any third-party JavaScript framework. This means you can use popular packages such as Knockout, Angular or Breeze.js to implement these patterns.

The Models and ViewModels in Shared project work seamlessly across the other projects with little or no modification. This leaves only code in the Views that may need duplication or customization. Sometimes this is unavoidable. There are vastly different device sizes and form factors to consider. There are also wide varieties of input devices including touch, keyboard, mouse, voice and so on. Code to handle these variables often needs to be separate.

In JavaScript apps, you can put Process Lifecycle Management code in the default.js in each individual project. You can also store models, ViewModels and any app logic there. The OSes handle protocol activations differently. However, most projects have some general-purpose utility code, which should be added to a script in the Shared project.

There are several other ways to share code across projects:

  • Conditional Compilation: Segment blocks of code to compile specifically for one OS or another. This applies to C#/Visual Basic and C++. Use regular if statements in WinJS. Learn more about this at bit.ly/UzdBAa.
  • Runtime Components: Components built with the Windows Runtime expose code to both Windows and Windows Phone apps, which allows code sharing.
  • Add Link: You can treat a pointer to a file in another project as if it were part of the current project.
  • Portable Class Library (PCL): This is the Microsoft .NET Framework way to share code. The Windows Runtime (WinRT) supports these legacy components, so you can use them in your apps. These are best to use if you already have an existing PCL. You might want to add WinRT components if you’re creating new components.

Clearly, a Shared project isn’t the only way to share code. You can reuse .NET code you already have lying around. For example, you may have some hefty performance requirements when creating a live action game that needs the help of C++. UsingWinRT components is a great way to share that kind of code.

Use Fragments whenever possible. These are just snippets of HTML both projects can use. UI components such as appBars and Flyouts don’t work well across form factors, so you’ll want to keep those in their corresponding projects. Code that sets general app settings and the like can go in the Shared project.

Wrapping Up

While Universal Apps don’t exactly provide a “write once and magically run everywhere” scenario, they do come close. There are some API changes, but with the single Windows Runtime behind the apps, it’s quite easy to work on an app that runs in the entire Windows ecosystem. You can even get your apps running on non-Microsoft platforms with the help of Visual Studio extensions from Xamarin. Also promised at Build was future support for Xbox One Achievements, Challenges, OneGuide and more. There are exciting things happening for Microsoft app developers.


Rachel Appel is a consultant, author, mentor and former Microsoft employee with more than 20 years of experience in the IT industry. She speaks at top industry conferences such as Visual Studio Live!, DevConnections,MIX and more. Her expertise lies within developing solutions that align business and technology focusing on the Microsoft dev stack and open Web. For more about Appel, visit her Web site at rachelappel.com.

Thanks to the following Microsoft technical expert for reviewing this article: Frank La Vigne