Click here to Skip to main content
11,632,778 members (81,631 online)
Click here to Skip to main content

3 Easy Ways to Store Data Locally in a Windows Phone App

, 12 Apr 2014 CPOL 3.7K 5
Rate this:
Please Sign up or sign in to vote.
Our applications – no matter whether games, productivity tools or business software – are all about data. As a result, when developing a program of any kind we spend most of the time solving data-related problems like where to get it, what to do about it, how to show it to our users and where to sto
Our applications – no matter whether games, productivity tools or business software – are all about data. As a result, when developing a program of any kind we spend most of the time solving data-related problems like where to get it, what to do about it, how to show it to our users and where to store it. Here I want to discuss the ways to get along with the last task and focus only on the ways to persist your application’s information locally. In Windows Phone this is in fact incredibly easy to do, although there are still several options to pick from and the choice depends on both how complex your data structures are and how and when you plan to utilize them.



Settings


The first approach to storing data in a Windows Phone application uses the IsolatedStorageSettings. While this one imposes certain restrictions on the developer, it is at the same time probably most widely used one. The reason is that almost any application has a bit of settings – both available to user and those utilized by the app internally. IsolatedStorageSettings actually gives you a key-value storage to save some named values and makes them easily available to you. This approach is most appropriate for storing simple stuff like bools and strings, although with the help of data contracts it is possible to save complex objects in the settings dictionary as well. The following code snippet shows how you can put some values into the IsolatedStorageSettings:

//using System.IO.IsolatedStorage;

IsolatedStorageSettings.ApplicationSettings["DontAskForRating"] = false;
IsolatedStorageSettings.ApplicationSettings["UserName"] = "Just Someone";
IsolatedStorageSettings.ApplicationSettings["WinsCount"] = 9000;

Reading settings is no more difficult than storing them. One thing to keep in mind here is that you have to store the values before you try to retrieve them. Fortunately, like any dictionary, the one exposed by IsolatedStorageSettings allows to check whether some key is present in it:

if (IsolatedStorageSettings.ApplicationSettings.Contains("DontAskForRating"))
{
    var booleanSetting = (bool)IsolatedStorageSettings.ApplicationSettings["DontAskForRating"];
}

if (IsolatedStorageSettings.ApplicationSettings.Contains("UserName"))
{
    var stringSetting = (string)IsolatedStorageSettings.ApplicationSettings["UserName"];
}

if (IsolatedStorageSettings.ApplicationSettings.Contains("WinsCount"))
{
    var intSetting = (int)IsolatedStorageSettings.ApplicationSettings["WinsCount"];
}

The application will automatically persist the updated values when closing, although if you want to save them right away you are free to call the Save() method. To keep things simpler, easier to maintain and more readable I tend to encapsulate all the messing with application settings in my own helper class, which usually looks something like this:

class MySettings
{
    private const string _DontAskForRatingKey = "dontaskforrating";
    private const string _UserNameKey = "username";

    private IsolatedStorageSettings AppSettings
    {
        get
        {
            return IsolatedStorageSettings.ApplicationSettings;
        }
    }

    public bool DontAskForRating
    {
        get
        {
            if (AppSettings.Contains(_DontAskForRatingKey) == false)
            {
                DontAskForRating = false;
            }
            return (bool)AppSettings[_DontAskForRatingKey];
        }

        set
        {
            AppSettings[_DontAskForRatingKey] = value;
            AppSettings.Save();
        }
    }

    public string UserName
    {
        get
        {
            if (AppSettings.Contains(_UserNameKey) == false)
            {
                UserName = "Just Someone";
            }
            return (string)AppSettings[_UserNameKey];
        }

        set
        {
            AppSettings[_UserNameKey] = value;
            AppSettings.Save();
        }
    }
}

Here in getters I check the corresponding keys, so that if one is absent I first set the default value. Besides, it is better to keep your settings’ keys in some constants so that they are defined in one place and can be changed consistently with little effort. Finally, introducing the shortcuts like AppSettings, which reduce the amount of clutter in code and make it much easier to read always seems a good idea to me.

Even though the IsolatedStorageSettings dictionary’s value type is object and you can potentially stick anything DataContract-serializable into it, it is not a very good idea to store large and complex entities in settings. If you decide to do this you should keep in mind that the app’s settings are retrieved in the process of starting up the application so that they are available to the application once it is loaded. This means that the harder is the job of loading the objects the longer will be the loading time of the application and you won’t have a way to control it (what if deserialization throws an exception?). This said, we'd better move the complex entities out of the settings – in this case you can make the decision on when they should be restored or saved on your own.

Overall, according to recommendations from the MSDN, the IsolatedStorageSettings are most suitable for storing little pieces of data, which are required when the app starts or shuts down – at these moments the key-value storage is entirely accessible to you. If you want to know more about this approach, check this article on Nokia Developer portal.


Simple types serialization


Now let’s switch to file-based storage. Windows Phone gives developers access to application’s individual file system through the IsolatedStorageFile class. This one allows you to create, read, write and remove files in a way very similar to that used when writing, say, desktop programs. This basically means that you are free to store your data in whatever form that is writable to a traditional file – for instance, you can freely push text or bytes to the stream, acquired through opening a file. At the same time such a low-level approach is rarely convenient and maintainable, which brings us to serialization. The de-facto standard for serializing data in Windows Phone applications is the DataContract-based serialization, which I will cover in the next section. For now I will stick to the XmlSerializer just to demonstrate that almost any serializer is suitable here. What I want to offer you is to store a chunk of built-in types – for example, a list of strings. This option, while being not that widely applicable, is very convenient and easy to implement in some cases, because it does not require any data contract at all:

public void SaveStrings()
{
    var quiteImportantStrings = new List<string>
    {
        "Audi", "BMW", "Mercedes Benz", "Porshe", "Volkswagen", "Opel"
    };

    using (var storage = IsolatedStorageFile.GetUserStoreForApplication())
    {
        if (storage.FileExists("awesomefile.xml"))
        {
            storage.DeleteFile("awesomefile.xml");
        }

        using (var file = storage.CreateFile("awesomefile.xml"))
        {
            var serializer = new XmlSerializer(typeof(List<string>));
            serializer.Serialize(file, quiteImportantStrings);
        }
    }
}

Quite simple, right? The best use-case for this approach is when your application handles some objects, which are easily and unambiguously represented by a built-in type. It is even better if you need to convert between the objects and such representation anyway because the latter is somehow utilized by the application. In this case, using DataContract serialization would mean an unnecessary overhead, so leaning towards this approach is a good option saving you from writing extra code and covering your objects in a layer of attributes. On the other hand, when compared to IsolatedStorageSettings, which you could use to store the same representations of your objects, it has the advantages of controllable execution. This seems especially important when your collection might easily grow with time, So if you just have a plain list of things, which can be represented by, say, strings, consider this option, even though it shows a degree of Primitive Obsession. Loading things from file with XmlSerializer is no harder than saving them:

public IEnumerable<string> LoadStrings()
{
    using (var storage = IsolatedStorageFile.GetUserStoreForApplication())
    {
        if (storage.FileExists("awesomefile.xml"))
        {
            using (var file = storage.OpenFile("awesomefile.xml", System.IO.FileMode.Open))
            {
                try
                {
                    var serializer = new XmlSerializer(typeof(List<string>));
                    return (List<string>)serializer.Deserialize(file);
                }
                catch (Exception)
                {
                    //You'd better do something more clever here
                    return new List<string>();
                }
            }
        }
        else
        {
            return new List<string>();
        }
    }
}

As you can see, storing stuff with the combination of IsolatedStorageSettings and XmlSerializer is pretty easy to do. The use cases for this approach are limited, because it sits between using IsolatedStorageSettings and DataContract serialization, which are both very powerful and can satisfy most needs of a developer. Still, there is a niche for this kind of serialization too – it works pretty well when one needs to store potentially long and not-always-needed collections of primitive objects, saving one from both setting up a DataContract and wasting space in the IsolatedStorageSettings.


Data Contract serialization


Now we can finally turn to the approach, which you will likely use most of the time – that is the combination of DataContract serialization with the use of IsolatedStorageFile. The files handling part of the story is absolutely similar to what we’ve just seen in the previous section. You just check whether a file with a particular name exists with IsolatedStorageFile.FileExists(..) method, open it with IsolatedStorageFile.OpenFile(..), create with IsolatedStorageFile.CreateFile(..) or delete with IsolatedStorageFile.DeleteFile(..). What is different is that to use DataContractSerializer on your own entity you have to define the data contract first. This is done through applying proper attributes to the class, which you are going to save and load. The class itself is marked with DataContractAttribute and the properties and fields, which you need to serialize, should bear the DataMemberAttribute:

[DataContract]
public class ImportantData
{
    [DataMember]
    public int GamesTotal { get; set; }

    [DataMember]
    public double WinsPct { get; set; }

    [DataMember]
    public string PlayerName { get; set; }

    [DataMember]
    public bool IsPremiumUser { get; set; }
}

Once you have the DataContract set up, you can serialize the instances of the corresponding class as easily as you did with plain collections:

//using System.Runtime.Serialization;
//using System.IO;
//using System.IO.IsolatedStorage;

public class StoreWithDataContract
{
    public void SaveData(ImportantData data)
    {
        try
        {
            using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (storage.FileExists("important.xml"))
                {
                    storage.DeleteFile("important.xml");
                }

                using (var file = storage.CreateFile("important.xml"))
                {
                    var serializer = new DataContractSerializer(typeof(ImportantData));
                    serializer.WriteObject(file, data);
                }
            }
        }
        catch (Exception) 
        {
            //your clever error handling goes here
        }
    }

    public ImportantData LoadData()
    {
        try
        {
            using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (isf.FileExists("important.xml"))
                {
                    var serializer = new DataContractSerializer(typeof(T));
                    using (var file = isf.OpenFile("important.xml", FileMode.Open))
                    {
                        return (ImportantData)serializer.ReadObject(file);
                    }
                }
            }
        }
        catch (Exception)
        {
            return new ImportantData();
        }

    }
}

The advantages of DataContract based approach are clear: it allows one to store quite complex objects – even the whole graphs of them – as well as thoroughly control their representation in one place without any imperative code and related duplication. The DataContract and DataMember attributes are really quite handy and don’t make the classes code much harder to read. Besides, there are several kinds of DataContractSerializer classes, allowing you to pick the most suitable data format – for example, you can use either traditional DataContractSerializer to store your information in XML, or switch to JsonDataContractSerializer and have all your data saved in JSON. Precisely as you would expect changing the serializer does not make you alter DataContract in any way (well there are dates, but…)

As far as DataContract serialization is concerned, there is one thing to keep in mind if you have some Windows Communication Foundation experience. In WCF this approach allows you to serialize not only public data members, but the protected and private ones as well, which makes it particularly useful in comparison to XmlSerializer that can’t peek at the guts of your objects. Windows Phone, however, is a different story and here DataContractSerializer can’t easily access protected and private data. Even though I find this quite constraining sometimes, it makes one better separate interfaces from implementation thus increasing the quality of our software.

So...


These are the 3 options, which I wanted to cover. At the same time, there exists at least one more technique, which plays better under certain circumstances: that is using a database embedded into your app. While there are plenty of engines to choose from, I believe SQLite is the most popular one. Here is an article, which covers the process of setting it up in your application. Finally, although you will most likely need some kind of local storage and use one of the methods described above to implement it, you still can decide to move most of your data to an external storage – a Cloud-based one or a database running on your personal server. There is almost infinite choice of tools and approaches, so you can certainly find one that suits your requirements best, but this topic falls out of scope of this post.

To help you going further, there are very nice slides by Matteo Pagani from the #wpdevfusion event, which compare the approaches to store Windows Phone app data in a shorter and more concise way than I do. Matteo also has a great deal of Windows Phone development materials in his blog – I really recommend it if you do develop apps for this great platform.

Please share your opinion on the best way to keep Windows Phone application data around. I will also highly appreciate suggestions or comments on the approaches, which I describe – feel free to let me know of any mistakes that I may have made here.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Alex Turok
Software Developer Acumatica
Russian Federation Russian Federation
I am a full-time .NET programmer and a lover of C#, C++, Clojure and Python at the same time. I do some Windows Phone development on my own and occasionally try myself at web-development. To enhance all these software-related activities I maintain a blog writing there on various topics, most of which actually come back to programming.

You may also be interested in...

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150728.1 | Last Updated 12 Apr 2014
Article Copyright 2014 by Alex Turok
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid