Port This!

A recent post on the .NET Framework blog is titled “Leveraging existing code across .NET platforms.” Code portability is a favorite topic of mine, although I’d missed their earlier announcement of a new portability analyzer. The tool, the .NET Portability Analyzer, or apiport.exe, can be used from the command line and is also now integrated into Visual Studio.

I’ve done several ports of .NET code for the DevForce framework, the first from .NET 3.5 to Silverlight 2, and more recently .NET 4.5 to Windows 8.x and Phone 8.0. The usual approach was to create a new project for the targeted platform and then see how large the explosion was when trying to compile. The analyzer saves you from this preliminary work and quickly scans your assemblies, creating a detailed report on API differences along with a few recommendations.

I thought it would be interesting to run the analyzer on the DevForce “client” assemblies. We ported these assemblies to other platforms the old fashioned way, so I’d see how thorough the tool is. As it turns out, it’s very thorough, quite fast, and easy to use.

What’s shown here should not be construed as a DevForce product roadmap.

The summary, while interesting, is not all that helpful, but I’ll get to the detail in a moment. Here I included all possible platforms the analyzer supports, although when porting your own assemblies you’re probably not interested in all possible targets, nor in porting to all of them at once. You might also find you’ll want to port from, say, full .NET to Windows 8.1, and then from there to a Xamarin platform. It’s usually easier to port from an assembly which is already focused on a mobile, “reduced” .NET API, and the API gap may be less daunting.

The “IdeaBlade.Core” assembly has the lowest API compatibility numbers in the list above, so I wanted to look at what the analyzer found. As the name of this assembly might imply, it’s responsible for a number of lower-level features, including configuration, reflection, WCF, MEF, registry access, some file I/O and EventLog access, a bit of remoting, use of the Cryptographic API, and so on.

Running apiport.exe from the command line generates Excel output, which is much more useful if you do plan on acting on the information, but I like the red light / green light look of the icons from the output generated within VS, so that’s what I show here. Here’s a snip of the detail view.

You’ll see long swaths of red for unsupported features in a specific platform, along with recommendations. For example, here System.Type, along with much of the Reflection API, is radically different in Windows 8 and Windows Phone 8.1.

You may also see large grids of red, for features found only in full .NET, such as System.Configuration.

You’ll also sometimes see surprises, maybe even showstoppers.

Hmm, System.Linq.Expressions is not available in Xamarin.iOS.


What? No WCF support in Phone 8.1!

Once you know what’s missing, the question then turns to how to mitigate the differences. I’ve found that there doesn’t seem to be one single best practice here, and developer and team productivity should trump any search for architectural purity. If a developer doesn’t know, and can’t easily discover, that a certain piece of code is used differently for different platforms, then your team will waste time with broken builds and worse.

If you aren’t doing continuous integration, now is also the time to start. You’ll need to port your test suite to the new platform(s) too. With the increased testing/build workload, team members may sometimes cut corners in rushed situations, and automatic builds and tests will truly be a lifesaver.

Especially when porting from full .NET, first look at reducing or eliminating functionality. Your tablet or mobile app can’t, and shouldn’t even need to, use the registry, a .config file, write to the console, etc. You can often just not include those files containing unnecessary functionality in the target project. Problem solved, unless internal politics or your customers take issue.

You should also usually avoid the temptation to “roll your own” for features missing in the platform. You really won’t miss things like the PropertyDescriptor, so apply the YAGNI principle with rigor.

You might also find that some features of your application may really be “desktop only” or “server only” features. If possible, it could be a good time to restructure and refactor any assembly such as this into multiple assemblies, leaving remaining “client” or “common” functionality easier to port. In fact, I wish we’d done this with IdeaBlade.Core.

Next, you’ll find some types supported across platforms, but not all the methods or overloads you might be used to. Or maybe there’s some slightly different way of accomplishing the same thing. Here the analyzer’s recommended changes can help. It’s usually easy enough to use Dispose rather than Close on a TextWriter; List<T> instead of ArrayList; or a different but functionally equivalent constructor or method overload. Having common code, rather than lots of platform-specific code, is much easier to maintain.

Speaking of the TextWriter and I/O in general, it could also be a good time to refactor for async, and possibly drop sync usage where possible. All the listed platforms support async-await (SL5 requires a compatibility pack).

When you do need platform-specific code, a very common approach is of course the use of compiler directives. These do have their place, but can also be overused, especially as the number of supported platforms grows. Do you really want to maintain code like this?

#if NET
 // do some .NET thing
#elif SILVERLIGHT
 // do some SL thing
#elif WINDOWS_APP
 // and so on
#elif WINDOWS_PHONE_APP
 // 
#elif ANDROID
 //
#endif 

Or worse,

#if NET || ANDROID
 // some cool thing
#elif SILVERLIGHT && !WINDOWS_PHONE
 // something else
#elif NETFX_CORE || WINDOWS_PHONE 
 // and so on
#endif

Or this?!

Other than compiler directives, what else can you do?

  • When entire classes will be wildly different across platforms, use interfaces and custom implementations for each platform.
  • When some code is common, you can use abstract base classes with subtyping by platform.
  • If only bits and pieces of a class will be platform-specific, you can make your classes partial and refactor platform functionality into functions for which you can use partial methods. Partial methods don’t seem to be used much, but they’re handy, and can be defined for static methods too.
  • Extension methods can be useful too, with the platform-specific functionality in separate extension classes. You can also use extension methods to add functionality that you think is “missing” from one platform. This lets calling code use a common API fairly seamlessly.
  • You can also use the adapter pattern, which works well for static functions. For example, a “TaskAdapter” can wrap the static methods of TaskEx in SL, and Task everywhere else.
  • What about “missing” interfaces and attributes? For example, ICloneable is “missing” on most of the platforms, but if you’ve got a lot of code that wants to Clone, defining the interface yourself is an option. The same with attributes, especially those that only define simple types and contain no logic or behavior. If you have a lot of code already decorated with “missing” attributes and don’t want to wrap compiler directives around every single one, creating your own implementation of the attribute is an option.
  • With all of the above, the next question might be whether to use separate files for each platform, and what naming scheme if so, or whether to continue using a single file with compiler directives separating content. I don’t have a good answer for this, although I lean toward using a single file with compiler directives, since it’s then obvious to developers working in these files where the platform-specific code is located.

Finally, one important question when looking at porting is whether a native assembly is truly the right approach, or should you take the plunge with a portable class library? The tooling around PCL is much improved, although profile-specific documentation is lagging. Porting a .NET library to a PCL, of any target profile, can be painful, and the analyzer doesn’t (yet?) handle this, possibly because the capabilities of each PCL profile are something you have to discover for yourself as a kind of initiation rite. A PCL can sometimes mean taking a lowest common denominator approach, and you might lose functionality. Nevertheless, “write once, run everywhere” is a worthy, if still somewhat elusive, goal.

Advertisements

First Look at Xamarin.Forms

Included in last week’s Xamarin 3 release is Xamarin.Forms. What is Xamarin.Forms? “A cross-platform natively backed UI toolkit abstraction that allows developers to easily create user interfaces that can be shared across Android, iOS, and Windows Phone” (emphasis mine).

Other goodies in Xamarin 3 are a visual designer for Xamarin.iOS within both Visual Studio and Xamarin Studio, NuGet support, F# support, support for Shared Projects, and quite a few fixes and enhancements. As usual with Xamarin, there’s a lot of promise here, along with many rough edges.

As in prior releases, you still need a Mac to serve as a “Xamarin build host” for iOS support, but this is an Apple-imposed restriction. If you’re doing Windows Phone 8 (or Windows Phone Silverlight 8.1) you need to be working in Windows 8 and Visual Studio with the appropriate Phone SDK installed. There’s no support for Universal apps, Windows Store, or Windows Phone 8.1 store apps at this time.

It’s probably just me, but I find all the Xamarin version numbers confusing, and didn’t even know that I’d been working in Xamarin 2 previously. “Xamarin 3.0” includes Xamarin Studio 5.0 and Xamarin VSIX 3.0 for Visual Studio support. Xamarin.Android is currently at 4.12, while Xamarin.iOS seems to be at 7.2.1.

Licensing also hasn’t changed, unfortunately. The Business edition, needed for Visual Studio support, still runs $999 per platform, per developer. The free Starter edition does not include support for Xamarin.Forms, while the Indie edition, which does, is $299 per platform, per developer.

The goal of Xamarin.Forms is rapid application development, and primarily targets enterprise apps. XF contains a gallery of about 40 common controls and a navigation abstraction, basically the essentials for a typical forms-style page or application. Controls in the gallery include:

  • Pages – an abstraction of the Android Activity, iOS ViewController, or WP Page
  • Layouts – composable containers such as Grid and StackLayout
  • Views – the usual buttons, labels, text boxes, images, date and time pickers, and more
  • Cells – combine a label with another visual element in tables and lists

Some might argue that there’s nothing particularly sexy about form controls, but allowing a developer to build the boilerplate parts of a UI once with non-platform specific abstractions – a Page, a Label, a ListView, etc. – is pretty powerful. Add in rendering using the native controls of each target platform, and this really is quite sexy. Although Xamarin seems to see Forms as a prototyping tool, if you can rapidly build native apps for multiple platforms – with native user interfaces, native API access, and native performance – while working at the “right” level of abstraction, what’s not to like?

Customization per platform is provided too. You can write custom renderers, and drop into platform-specific implementations via the built-in DependencyService. The DependencyService is not intended for use as a general-purpose DI container, it’s more in the style of “platform enlightenment” used with some PCL implementations, although it can be used as a simple Service Locator too.

There are two additional features though, which make Xamarin.Forms really exciting: data binding and XAML.

Android and iOS have no built-in data binding support, although you can use a third party framework like MvvmCross to accomplish this. The data binding support in Xamarin.Forms feels quite familiar: one- and two-way binding, INotifyPropertyChanged, INotifyCollectionChanged, value converters, static resources, ItemsSource, data templates. Missing, however, is validation, and there’s no INotifyDataErrorInfo implementation.

XAML! Most of the current Xamarin.Forms doc and samples seem to focus on writing the UI in code, but what’s better when building a UI than working in a visual designer to get immediate feedback on every change? Granted, wiring up your controls in code is powerful, but it’s not 1990. XAML to the rescue! Or maybe not quite yet. Tucked away in the documentation is the note “Xamarin.Forms is not compatible with pre-existing XAML visual designers.” Yep, the XAML for Xamarin.Forms cannot be opened in the visual designer of either Visual Studio or Xamarin Studio. At this time you must hand edit your XAML. Although there is supposed to be Intellisense within the XAML code editor now, I couldn’t get it working. A few other missing pieces: breakpoints and debugging of XAML data bindings aren’t supported yet, and the current API-level documentation doesn’t show any XAML sample code to make hand editing easier.

It’s tempting to want to grab some XAML from an existing app, forgetting that Xamarin.Forms defines its own controls. So for example this:

<StackPanel Orientation="Vertical">
   <TextBlock Text="Name"  />
   <TextBox Text="{Binding FullName}" />
</StackPanel>

might look like this in Xamarin.Forms:

<StackLayout  VerticalOptions="FillAndExpand" >
  <Label Text="Name" />
  <Entry Text="{Binding FullName}"  />
</StackLayout>

Also note that XAML files can be defined only in portable class libraries and shared projects: platform-specific projects don’t understand Xamarin.Forms.

Which brings me to another important piece of Xamarin.Forms: shared project support, also introduced by Microsoft in Visual Studio 2013 Update 2 for Universal Apps. Previously, platform-specific projects could reference a portable class library for shared content; with Xamarin 3 there’s now the option to use shared projects, in both VS and Xamarin Studio.

A “shared project” is not truly a VS or XS project, and won’t compile into a separate assembly. Instead the project, with a .shproj extension, imports a .projitems file containing all the shared project artifacts. Projects referencing the shared project will automatically include all shared artifacts. It’s a little like file linking, a common technique for sharing content among multiple platform-specific projects, and also supports compiler directives. Unlike file linking, the editing experience is much better, as the code editor now includes a “context switcher” which allows you to view and edit the file within the context of the target project.

As with Universal Apps, choosing between a PCL or shared project for shared content is a decision each team must make. A PCL can provide quite a bit of flexibility and potential reuse in other solutions and platforms and is usually separately testable, but also means deploying another assembly. A shared project allows you to compile each target project into a single assembly, so might be great for small apps.

One gotcha with a shared project is that the referencing target projects likely have different namespace and/or assembly names. If, for example, you define a local namespace in shared XAML (e.g., xmlns:local="clr-namespace:TipCalc;assembly=TipCalc") for shared value converters, you’ll need to use the same assembly name for all target projects too.

So, XAML and data binding, PCL and shared projects. What might a simple solution look like? While I haven’t yet written anything that’s not both derivative and butt ugly, I found these helpful:

  • Introduction to Xamarin.Forms – This is really quite comprehensive, and the best place to start.
  • Among the samples, TipCalc shows off XAML and data binding, while the Forms Gallery provides code for every control type. Both samples were written by Charles Petzold, which should be reason enough to go check them out.