Generalizing storage access for Windows Phone and WinRT apps

When building application that works both on WinRT and Window Phone you use Portable Class Libraries (PCL) for shared classes. As there are many application specific things that are not same on different platforms or that are not supported by PCL then you have to make some architectural decisions when creating shared functionalities. In this posting I will focus on persisting data for offline use.

Problem: Different storage implementations

As WinRT and Windows Phone application both use same data storing logic it is implemented in PCL to avoid duplicating the code. The class that needs to load and save data is defined in PCL but there is no generic storage interface or class defined in PCL that WinRT and Windows Phone applications can use.

pcl-storage-winrt-wp

The problem is: how to make storage operations on different platforms available to shared library that is used by applications running on different platforms and having different storage logic.

Breaking dependency to storage logic

Usually we have already one application written to some point when starting with another. When starting with another application we move shared functionality to PCL and find out that most of our original platform classes are not available there.

public interface IStorageProvider
{
   
byte
[] GetData();
   
void SaveData(byte[] data);       
}

Next we make our DataManager class to use this interface to communicate with storage.

public class DataManager
{
   
private readonly IStorageProvider
_provider;

   
public DataManager(IStorageProvider
provider)
    {
        _provider = provider;
    }

   
// ...
}

When DataManager class is created we give it some class that implements IStorageProvider interface and DataManager is happy.

Example: Windows Phone storage provider

Windows Phone uses isolated storage for application settings and files. Isolated storage is supported only in Windows Phone and therefore it is not supported by PCL. We have to define storage provider for Windows Phone in our Windows Phone application.

public class WindowsPhoneStorageProvider : IStorageProvider
{
   
public byte
[] LoadData()
    {
       
using (var storage = IsolatedStorageFile
.GetUserStoreForApplication())
        {
           
if (!storage.FileExists("data.dat"
))
               
return null
;

           
using (var file = storage.OpenFile("data.dat", FileMode.Open, FileAccess.Read, FileShare
.None))
            {
               
var data = new byte
[file.Length];
                file.Read(data, 0, data.Length);
               
return
data;
            }
        }
    }

   
public void SaveData(byte
[] data)
    {
       
using (var storage = IsolatedStorageFile
.GetUserStoreForApplication())
        {
           
if (storage.FileExists("data.dat"
))
                storage.DeleteFile(
"data.dat"
);

           
using (var file = storage.OpenFile("data.dat", FileMode.Create, FileAccess.Write, FileShare
.None))
            {
                file.Write(data, 0, data.Length);
                file.Flush(
true);
            }
        }
    }
}

Example: WinRT storage provider

On WinRT we must write different code to use application storage. Here I’m using local storage for my data file.

public class WinRTStorageProvider : IStorageProvider
{
   
public string
UserName
    {
       
get { return ""
; }
    }

   
public async void SaveData(byte
[] data)
    {
       
var storageFolder = ApplicationData
.Current.LocalFolder;
       
var file = await storageFolder.CreateFileAsync("data.dat", CreationCollisionOption
.ReplaceExisting);
       
using (var stream = await file.OpenAsync(FileAccessMode
.ReadWrite))
       
using (var writer = new DataWriter
(stream))
        {
            writer.WriteBytes(data);
           
await
writer.StoreAsync();
        }
    }

   
public async Task<byte
[]> ReadData()
    {
       
var storageFolder = ApplicationData
.Current.LocalFolder;
       
var file = await storageFolder.GetFileAsync("data.dat"
);
       
using (var stream = await file.OpenAsync(FileAccessMode
.Read))
       
using (var
inputStream = stream.GetInputStreamAt(0))
       
using (var reader = new DataReader
(inputStream))
        {
           
var data = new byte
[stream.Size];
           
await reader.LoadAsync((uint
)data.Length);
            reader.ReadBytes(data);
           
return data;
        }
    }
}

Wrapping up

Now we can use our DataManager in PCL library with different storage providers. DataManager knows storage providers by IStorageProvider interface, keeping itself away from storage implementations. Storage implementations are defined in specific applications or their libraries and these implementations are given to DataManager when data manager class is created. We can easily change internal storage logic if we need and we don’t have to modify code in another libraries.

Gunnar Peipman

Gunnar Peipman is ASP.NET, Azure and SharePoint fan, Estonian Microsoft user group leader, blogger, conference speaker, teacher, and tech maniac. Since 2008 he is Microsoft MVP specialized on ASP.NET.

    7 thoughts on “Generalizing storage access for Windows Phone and WinRT apps

    • Pingback:Generalizing storage access for Windows Phone and WinRT apps | TechBlog

    • December 10, 2013 at 8:35 am
      Permalink

      IStorageProvider has GetData() method with return type of byte[] but WinRTStorageProvider has GetData() method with return type of Task, so do I need to add more methods in interface and do I need to implement those unnecessary methods in both class?

    • December 10, 2013 at 10:55 pm
      Permalink

      Can you make this with serialized data? It would be very helpful.

    • December 10, 2013 at 11:03 pm
      Permalink

      You can easily turn serialized data as string to bytes and back. Check System.Encoding.UTF8Encoding.UTF8 methods.

    • December 11, 2013 at 10:28 am
      Permalink

      Farhan, with async methods you just return Task. There’s nothing wrong as compiler handles the situation.

    • December 11, 2013 at 5:48 pm
      Permalink

      But I am getting error that you didn’t implement all the methods of interface.

    • December 13, 2013 at 5:00 pm
      Permalink

      @Farhan In the WindowsPhoneStorageProvider wrap the LoadData in a
      await Task.Run(() => { /* code here */}); and return a Task that should work. And don’t forget to change the Interface.

    Leave a Reply

    Your email address will not be published. Required fields are marked *