Patterns for sharing code in Windows Phone and Windows 8 Applications

Background

With the latest batch of releases in Windows Phone 8 and Windows 8 Microsoft has been trying to close the gap between both development platforms. (The old “write once, run everywhere”-idea)
Although both platforms are based on the same Windows 8 kernel, there certainly are quite a few differences.

One key piece of interoperability between the platforms is the concept of a portable class library (or PCL). A portal class library is a component that you can link from both Windows Phone apps and Windows Store applications. Since both platforms are inherently different though, this does not allow you to code everything in the PCL.

What you can do essentially in a portable class library, is target everything that IS common between both platforms, for example:

  • You CAN write your validation logic because this is just plain .NET code.
  • You CANNOT write code against the File APIs, since both platforms have a different API for accessing the file system.

To solve this problem and to make sure that we only have to write our code once we will have to separate the implementation from the interface.

Example: unifying file system API’s

In this example I will show you how you can separate the differences and implement the common logic in one place. Since both platforms have different file system API’s, that’s where we’ll have to separate the differences. In order to do so we will be creating one interface that represents the common file operations: SaveFile and OpenFile. Next we will write a StorageManager that consumes this interface. In both client apps we will create an implementation of the interface so that the StorageManager can be reused on both platforms. This diagram illustrates the concept:

image

Now we can implement the access to both API’s in different classes and project them onto an Interface.

Note: Although the code for both APIs is really similar, you are required to write it inside either a Windows Phone project or a Windows 8 project. That means you can’t access it from your PCL, and thus you can’t write anything in your PCL that relies on storage. However, since our interface lives in the PCL we can consume the interface from within the PCL.

The following classes show an example:

Interface IStorageProvider

public interface IStorageProvider 
{
    Task SaveFile(string Name, string Content);

    Task<String> OpenFile(String Name);

}

PhoneStorageProvider

public class PhoneStorageProvider : IStorageProvider
{
    public async virtual Task SaveFile(string Name, string Content)
    {
        byte[] data = Encoding.UTF8.GetBytes(Content);

        StorageFolder folder = ApplicationData.Current.LocalFolder;
        StorageFile file = await folder.CreateFileAsync(Name, 
            CreationCollisionOption.ReplaceExisting);

        using (Stream s = await file.OpenStreamForWriteAsync())
        {
            await s.WriteAsync(data, 0, data.Length);
        }
    }

    public async virtual Task<String> OpenFile(String Name)
    {
        StorageFolder folder = ApplicationData.Current.LocalFolder;

        StorageFile f = await folder.GetFileAsync(Name);

        byte[] data = null;
        using (Stream s = await f.OpenStreamForReadAsync())
        {
            data = new byte[s.Length];
            await s.ReadAsync(data, 0, (int)s.Length);
        }
        return Encoding.UTF8.GetString(data, 0, data.Length);
    }
}

Win8StorageProvider

public class Win8StorageProvider : IStorageProvider
{
    public virtual Task SaveFile(string Name, string Content)
    {
        byte[] data = Encoding.UTF8.GetBytes(Content);
        
        StorageFolder folder = ApplicationData.Current.LocalFolder;
        StorageFile file = folder.CreateFileAsync(Name, 
            CreationCollisionOption.ReplaceExisting);

        using (Stream s = file.OpenStreamForWriteAsync)
        {
            return s.WriteAsync(data, 0, data.Length);
        }
    }

    public async virtual Task<String> OpenFile(String Name)
    {
        byte[] data = null;

        StorageFolder folder = ApplicationData.Current.LocalFolder;


        StorageFile f = await folder.GetFileAsync(Name);

        using (Stream s = await f.OpenStreamForReadAsync()) {
            data = new byte[s.Length];
            await s.ReadAsync(data, 0, s.Length);
        }
        return Encoding.UTF8.GetString(data, 0, data.Length);
    }
}

With these items in place we can now write a generic StorageManager that talks to the interface. Depending on the application we will pass in either a Phone8StorageProvider or a Win8StorageProvider. In the StorageManager we could for example (de)serialization of objects:

public class StorageManager<T> {

    private IStorageProvider _provider;
    
    public StorageManager(IStorageProvider storageprovider) {
        _provider = storageprovider;
    }

    public async Task SaveItem(T item) {
        String serializeditem = null;
        // serializeditem = serialize(item);
        await _provider.SaveFile("filename.txt", serializeditem);
    }

    public async Task<IEnumerable<T>> loadItems() {
        List<T> items = new List<T>();
        for (int i = 0; i < 10; i++)
        {
            String content = await _provider.OpenFile("filename" + i + ".txt");
            T deserialized = default(T);
            // deserialized = deserialize(content);
            // 
            items.Add(deserialized);
        }
        return items;
    }
}

For the sake of simplicity I have just included the basic idea in this class. You notice though how at this point the code is not focused on how to get the contents anymore but more on what to do with it.

 

Conclusion

In this example I showed how we can project two inherently different API’s onto the same Interface and then talk to that interface. This assures you that your logic does not rely on platform specifics. In order to do so we used the Adapter pattern:

We adapt the Windows 8 and the Windows Phone 8 File API to our interface. This is useful in a number of other cases as well such as when you want to work with a timer in a PCL.

  • http://xiu.shoeke.com Steve Hansen

    Because people might just copy the code without checking it, you have a few calls to Async methods in the Win8StorageProvider that aren’t awaited (WriteAsync, ReadAsync).

    And although it works, it’s not recommended to use async void unless you can’t change the signature (event handlers mostly) and you should use async Task (and Task SaveFile) so that you can also await writing the file.

    Otherwise the code just continues so you won’t know when it’s written AND if it throws an exception you can’t catch it.

    • Kenneth Truyers

      Hello Steve,

      Good catch! It’s definitely a good idea to always implement async voids as async Task.
      Since I was more focusing on the abstraction, I didn’t pay attention to this.

      I’ve corrected the code. Thanks for letting me know!

  • Pingback: Patterns for sharing code in Windows Phone and Windows 8 Applications

  • Pingback: Portable class libraries or source code linking?