APIMASH: Porting to Windows Phone 8
You may recall that in May my evangelist colleagues and I launched a series of workshops around a bevy of API Starter Kits designed to kickstart HTML5/JS and C# developers into building their own, unique Windows 8 applications. Since then we’ve seen a few of your efforts appear in the Windows Store including Because Chuck Said So and my very own Eye on Traffic (which leverages the TomTom Traffic Cameras API).
More recently I’ve been working on porting my Windows 8 Starter Kit (that leverages both the Bing Maps and TomTom APIs) to Windows Phone, and I thought I’d share some of my experiences in doing so.
In a previous post, I spent a bit of time describing the architecture of the Windows 8 version, and it was certainly my goal to get as much code reuse as possible, though of course, I knew that the form factor of a Windows Phone device would necessitate rethinking the user experience. For this post, I’ll split the discussion in two parts: the services layer that handles the API calls and the front-end user experience.
Services Layer
From my architecture overview, you’ll recall there are two primary class libraries involved:
- APIMASH which includes the plumbing code for issuing an HTTP request, parsing the response, and serializing the payload to the required formats, and
- APIMASH_APIs which includes the object models and API-specific code to obtain the desired data from the Bing and TomTom REST APIs
APIMASH Changes
For the base layer, APIMASH, there were two notable changes required.
Deserializing Byte Stream to a Bitmap
In the class to deserialize HTTP response payloads, I had a method that takes the binary response from a GET request for a jpg image and turns it into a BitmapImage. It turns out things work a bit differently between Windows 8 and Windows Phone in this regard.
As you might expect, the namespaces are different (Windows.UI.Xaml.Media.Imaging for Windows 8 and System.WIndows.Media.Imaging for Windows Phone), but it takes a bit more than a namespace modification to generate the same behavior.
The Windows Phone case is a near no-brainer:
public static T DeserializeImage(Byte[] objBytes)
{
try
{
BitmapImage image = new BitmapImage();
using (var stream = new MemoryStream(objBytes))
{
image.SetSource(stream);
}
return (T) ((object) image);
}
catch (Exception e) { throw e; }
}
But I’m not too proud to say the Windows 8 version had taken me a while to come up with. The primary challenge was that the argument to SetSource is an IRandomAccessStream versus a good ole System.IO.Stream Below is what you’ll find in the current Windows 8 implementation, thought I suspect I could clean this up a bit by leveraging WindowsRuntimeStreamExtensions.
public static T DeserializeImage(Byte[] objBytes)
{
try
{
BitmapImage image = new BitmapImage();
// create a new in memory stream and datawriter
using (var stream = new InMemoryRandomAccessStream())
{
using (DataWriter dw = new DataWriter(stream))
{
// write the raw bytes and store synchronously
dw.WriteBytes(objBytes);
dw.StoreAsync().AsTask().Wait();
// set the image source
stream.Seek(0);
image.SetSource(stream);
}
}
return (T) ((object) image);
}
catch (Exception e) { throw e; }
}
HTTPClient
Windows 8 has this awesome HttpClient class which provides a very simple REST-inspired interface (methods like GetAsync, PostAsync, etc.). Unfortunately, that’s not available in Windows Phone… well sort of.
There is a portable HttpClient that helps bring the light and airy HttpClient class to both Windows Phone and .NET 4, and that certainly seemed like the easiest approach for my porting effort. It does require adding a few dependencies like the Base Class Libraries, but the NuGet package makes that really simple to incorporate.
All seemed to work well at first, except that my cam images didn’t seem to refresh on demand; the existing image never refreshed, but other cam images were being pulled in just fine. That led me on a chase that included discovering that Windows Phone has this helpful feature called image caching (read about it here), and for a while I was convinced somehow that was the culprit. It was not.
Caching was indeed the root cause, but it wasn’t image caching, it was web response caching. Since each request for a given camera was hitting the same URI, and Windows Phone was caching the results, I was continually seeing the same image served from cache.
One workaround for this is to tack on a (meaningless) URI parameter that always changes, say using a GUID. Presuming the server responding to the URI just ignores the superfluous parameter, all is well. That seemed a bit hacky though and could have a performance or memory hit since the device would now be caching data it would never ever re-access.
The solution, elegant in its REST-fulness was a one-liner included before the call to GetAsync
httpClient.DefaultRequestHeaders.IfModifiedSince = DateTime.UtcNow;
APIMASH_APIs Changes
As you may know, the preferred mapping option for Windows Phone is not the Bing Maps control but rather the one provided by Nokia as part of the platform (and available on all Windows Phone 8 devices). Knowing I’d be reworking some of the map user experience code (and being somewhat time constrained), I opted to pull out the Bing Maps API layer altogether.
In the Windows 8 application, the Bing Maps API fueled the search experience - which I keenly wanted to highlight since it’s a Windows 8 platform differentiator - but for the Windows Phone version, I decided to scale back and drop the entire APIMASH_BingMaps.cs implementation. A coincident casualty of that decision (in conjunction with the use of Nokia maps) were two methods that no longer served any purpose for the Windows Phone versioh.
Beyond that, the changes to the APIMASH_TomTom.cs implementation were incredibly minor:
Imaging namespace modifications: System.Windows.Media.Imaging versus Windows.UI.Xaml.Media.Imaging.
Image source pathing: The implementation includes two canned “error message” images should something go wrong when requesting a camera image. In Windows 8, those resources are delivered as loose content and accessible via the ms-appx:/// URI scheme. For Windows Phone 8, I needed to mark them as Resource on build and reference via them via URsI like /APIMASH_APIs;component/Assets/camera404.png.
It is possible to create an implementation that will use resource streams and work across both Windows 8 and Windows Phone with no changes, but for the two images I’m dealing with here, it seemed like overkill.
Application resource pathing: For both targeted platforms, my code stores the developer API key for TomTom as a static resource in the App.xaml file, but there is a nuance of difference in how resources are keyed. In Windows Phone, the ResourceDictionary class can be keyed off of any object type; in Windows 8, the ResourceDictionary is a clamped down a bit to deal with KeyValuePair specifically, so there are semantic differences between the Contains methods, and the Windows RT version of the class throws in a new ContainsKey method. Frankly, it took longer to explain it just now than to address it in code!
User Experience
From the look of the app on Windows 8 (shown earlier in this post), it should be clear that that specific user experience wasn’t going to transfer directly to Windows Phone, and in general, you should anticipate the front-end work of a port from Windows 8 to Windows Phone (or vice-versa) to require more thought, reflection, and elbow-grease than the plumbing (e.g., business logic and services layer).
There isn’t a single formula for revamping a user experience for a different form factor, and there are lots of decisions large and small that are part of the process, many very specific to your application. I will say that despite the great feature set of the Windows Phone Emulator, once I began to focus on the UX changes (versus the services implementation), I very quickly adopted a workflow of deploying and debugging right from the device. What seemed to make perfect sense when interacting with the emulator via the mouse – or even touch, since I have a touch enabled laptop – seemed awkward when actually holding the device.
I settled on the following UX for the phone version of the Starter Kit, and it’s quite obviously not feature-equivalent with the Windows 8 version, but I don’t think it needs to be.Notably, there’s no list view of all the cameras, but for the context of a Windows Phone (versus Windows 8) user, I don’t think that list is particularly useful, and the location of the user in context with the map view is paramount.
To get to this point in my development, I started with a File->New Windows Phone 8 application, and one-by-one pulled in some of the existing assets from the Windows 8 application, including:
- Common classes like BindableBase.cs (no changes needed) and a few converter classes used in the XAML. Those do require a bit of tweaking because the implementation of IValueConverter differs between the two platforms (specifically the last parameter of both of the conversion methods).
- Custom map pin classes (CurrentLocationPin.xaml and PointOfInterestPin.xaml). One change was required here, namely the interpretation of the anchor point (from absolute pixels in the Windows 8 case, to a relative 0-1 scale in Windows Phone). Keeping in mind these assets are being used on two completely different map implementations, I was pleasantly surprised!
- Given the change to Nokia maps from the Bing Maps control, I expected a lot of rewiring, but since I had abstracted much of the map UI integration into a BingMapsExtensions class, the changes weren’t difficult and very localized. (I also decided to change the class name, since Bing Maps wasn’t in the picture any more!)
Reworking the screens though was pretty much a rewrite with a bit of cut-and-paste; here are some of the things you should be prepared for:
- Windows 8 and Windows Phone have similar but not quite identical process lifetime and navigation models,
- There are (often frustrating) nuances of difference in the XAML. For example, Windows uses using in namespace references, while Windows Phone uses clr-namespace. And you cannot always rely on feature parity across analogous user interface elements. In general though, Windows Phone XAML seems a bit more feature-rich than Windows 8, so I suspect my port from Windows 8 to Windows Phone was smoother than the reverse might have been.
- Windows 8 doesn’t leverage theming all that much (it’s either light or dark), but in Windows Phone you typically will want to tap into styles based on the user-selected theme, leveraging resources like PhoneAccentBrush.
- Be sure to check out the Windows Phone Toolkit for those things you can’t believe aren’t there by default :)
Beyond that, I’ll leave it to you to crack open both projects and take a look at how I handle specific elements of the implementation.
Whoa! What about these Portable Class Libraries I hear so much about?
For those of you keenly following the evolution and merging of the Windows 8 and Windows Phone development experiences, you might be wondering why I haven’t mentioned anything about Portable Class Libraries:
The Portable Class Library project enables you to write and build managed assemblies that work on more than one .NET Framework platform. You can create classes that contain code you wish to share across many projects, such as shared business logic, and then reference those classes from different types of projects.
Wouldn’t that have saved me a ton of time? Perhaps for the backend services implementation, but this wasn’t really a greenfield project, so did it (or does it in general) make sense to fix what ain’t broke: the Windows 8 version? For a real app that I’d expecte to evolve on both platforms, it might be worth going back and doing so to reduce the amount of duplicate code.
But I'm being a bit disingenuous here as well, I pretty much knew I’d be evolving this to Windows Phone soon after starting the Windows 8 version, so the real answer is a bit more tactical and job-related. Portable class libraries are a Visual Studio Professional (and above) feature; in the interest of reaching as large an audience as possible with our Starter Kits, we wanted to make it as free and easy as downloading Visual Studio Express and hitting F5.
If you do want to learn more about Portable Class Libraries and techniques for sharing code between Windows 8 and Windows Phone projects, there are a number of great resources, including:
Dev Center (Windows Phone 8 and Windows 8 app development)
Real Talk: Sharing Code Between the Windows & Windows Phone Platforms (Build 2013)