Sharing code across platforms

At Build we announced two great ways to re-use your code: the new Universal Windows apps, and the improved portable class libraries. They both help you reuse code across platforms.

In this post, I’ll describe both options and how you can choose between them.

Overview

Why two options? The short answer is that shared projects are about sharing source code and assets, while portable class libraries are about sharing binaries:

  • Shared projects are great when your common code has a few bits of platform-specific code, since you can adapt it with #if.

  • Portable class libraries are great when your common code is platform-independent as well as for reusable libraries where the platform-specific code can be factored out.

Visual Studio 2013 Update 2 makes code sharing as simple as sharing cookies

Shared projects

Visual Studio has supported linked files for as long as I can remember. Linked files allow you have the same physical file be included into multiple projects. However, even Visual Studio 2013 only supported linking individual files, which tends to become cumbersome when multiple files and projects are being used.

Visual Studio 2013 Update 2 introduces the new feature Universal Windows apps. It’s implemented via a general mechanism called shared projects. Shared projects are linked files on steroids. Instead of linking individual files, you can place them inside a shared project. The files are then linked as single entity by adding a reference to the entire shared project.

Behind the scenes, Visual Studio still thinks of them as linked files which means that many of the improvements we made also apply to linked files. If all you need is to share a single file, for example AssemblyInfo.cs, then linked files are still an excellent tool. We’ve also removed the notorious “the file is already open” error message when you open the same file from a different project. There are also some really cool new features around how the IDE presents the shared code. Make sure to check out the post on Universal Apps.

The major benefit of shared projects is that you can easily handle cases where your code needs to call different APIs, depending on the target platform. You can do this via conditional compilation:

public class NavigationHelper
{
    public NavigationHelper()
    {
        #if WINDOWS_PHONE_APP
            Windows.Phone.UI.Input.HardwareButtons.BackPressed
                += HardwareButtons_BackPressed;
        #elif WINDOWS_APP
            // Ignore. Windows Store doesn't have support for this.
        #else
            #error Unknown platform
        #endif
    }

    // ...
}

The preprocessor symbols, such as WINDOWS_PHONE_APP, are defined by the project and are usually already provided by the template. You can find them on the Build tab in the project properties:

Note: Right now, shared projects are only supported in the context of building Universal Apps. Moving forward, this may become a general purpose feature. There is a preview of how this may look like.

Portable class libraries

One source code, one binary, multiple platforms. That’s the promise of portable class libraries. In Update 2, we’ve improved portable class libraries quite a bit:

  1. You can now consume & produce WinRT APIs
  2. You can now create & design portable XAML user controls
  3. You can now easily convert a platform-specific class library into a portable class library by adding another platform:

Portable class libraries present the developer with the intersection of APIs that can be shared across all the selected targets. This allows producing a single binary that is guaranteed to run on all of the targets. Producing a single binary gives you the following benefits:

  • You can easily redistribute the component to 3rd parties, outside your solution. This includes sharing the binary directly as well as sharing it via a package manager like NuGet. Either way, consumers just add a reference to it and are good to go. Thanks to the “C” in “CLR” it also doesn’t matter which programming language the library was written in.

  • It’s the same component, on all platforms. This makes it super easy to reason about your components. It also streamlines testing quite a bit.

Good examples of libraries that take advantage of portable class libraries are JSON.NET and immutable collections.

Choosing between shared projects and portable class libraries

The rule of thumb is:

  1. If you have to use platform-specific APIs and you only need to share within the same solution, shared projects will have the best experience.

  2. If you don’t have to use platform-specific APIs or you need to share with consumers outside of your solution, portable class libraries provide the best experience.

When you have to share your code with consumers outside your solution and you need to use platform-specific APIs, you need to make a trade-off decision. The following table gives you some orientation by highlighting the strengths and weaknesses of each approach. Of course, you can also use a combination.

  Strengths Weaknesses
Shared Projects
  • Allows using platform-specific APIs by using #if
  • Can handle cases where the source is compatible but the binary wouldn’t be, for example, when APIs are in different namespaces
  • You have to either share the source or multiple binaries which forwards part of the complexity to the consumer
  • Requires convention and discipline to centralize handling of divergent APIs so that the code doesn’t become an unmaintainable mess
Portable class libraries
  • Extremely simple deployment and sharing model
  • Scales well to larger systems which requires multiple solutions, involves 3rd parties or multiple programming languages

Summary

Code reuse is a multifaceted domain and as such there isn’t a one-size-fits-all solution. As a developer, you want to be able to select the best tool for the job. Therefore we’ve updated Visual Studio such that it provides you with multiple options in order to be even more productive.

Starting with Visual Studio 2013 Update 2, you have first class support for sharing source code as well as for sharing binaries. Shared projects provide a light-weight and flexible way to bridge platform differences. Portable class libraries are great for building cross-platform components that can be easily deployed and reused by 3rd parties.

Use the power and share with love!