31 Days of Windows 8 | Day #8: Local and Roaming Data

This article is Day #8 in a series called 31 Days of Windows 8.  Each of the articles in this series will be published for both HTML5/JS and XAML/C#. You can find additional resources, downloads, and source code on our website.

advertisementsample

In several of the articles in this series, we have mentioned that storing data is not only incredibly important, but also that it’s super easy to do, both locally to a device, as well as roaming across the many different devices a user may use.

Microsoft offers us some specific guidance on when to use roaming vs. local storage, but I’ll give you a quick summary here so that you’ve had a chance to read it (because we both know you didn’t click that link).  Again, these are guidelines, so you’re not going to get denied from the store for breaking these rules, but there are also limits to data transfer size and speed.  Exceeding those will prevent your app from actually roaming data for a period of time.

DO

  • Use roaming for preferences and customization.  Any choice that a user is likely to make on each machine that they use should be roamed.  These are basic settings, like color preferences, favorite actors, or whether or not to publish data to Twitter.
  • Use roaming to let users continue a task.  Having my browser favorites follow me around, or even my high scores is awesome.  Allowing me to continue writing that email (or blog post) I never finished? Even better.

DON’T

  • Use roaming for information that is clearly local-only.  This includes things like file paths and other data that only makes sense to the local device.
  • Don’t roam large datasets.  There is a quota, which you can determine in code, that limits the size of your roaming dataset.  It is best to only roam preferences and small data files, as we will show in this article.
  • Don’t use roaming for instant synchronization or rapidly changing data.  Windows controls when and how often your app data will be roamed, so don’t count on instant synchronization.  Build a web service of your own if you need this kind of reliability.  Also, don’t update the roaming data constantly.  For example, you don’t need to roam the user’s current location at all times, instead update it every minute or so.  You’ll still provide a rich experience without destroying your quota.

One last thing to remember: the way data is roamed across devices is managed by the user’s Microsoft account.  If they log into two machines with the same account credentials, AND they install the same app in both places, THEN the roaming settings and files will travel.  Until then, nothing happens.

Now that I’ve scared you into never using this, let’s take a look at how it’s done.  There are two types of data that can be stored, and we will address each one of them both locally and roamed.  First up is Settings, followed by Files.

Local and Roaming Settings

When you hear the word “settings” in Windows 8 (or even Windows Phone) development, “small, simple data” is what should come to mind.  We’re really talking about storing name/value pairs.

Good examples of these are user preferences.  Perhaps you stored the user’s first name (a string value) so that you could address them as such in your game.  Maybe they decided to turn off notifications (a boolean value) from your app.  Settings are also one of the easiest ways to store data, and I’ve found myself on more than one occasion storing a great number of settings values in my applications. Because these are invisible values that live in an invisible data store, I’ve chained a set of methods together that will create, then read, and then delete the settings values, both locally and roaming.  You’ll see that it’s actually pretty simple to use.

ApplicationDataContainer settingsLocal;
ApplicationDataContainer settingsRoaming;
 
string currentBook;
int currentPage;
        
public MainPage()
{
    this.InitializeComponent();
 
    settingsLocal = ApplicationData.Current.LocalSettings;
    settingsRoaming = ApplicationData.Current.RoamingSettings;
 
    AddSettings();
}
 
private void AddSettings()
{
    //There is no reason to set the same data to both local and roaming.
    //This is here merely for illustration of HOW to do it.
    //You should make the choice as to whether your data should be roamed.
 
    settingsLocal.Values["currentBook"] = "Hitchhiker's Guide To The Galaxy";
    settingsLocal.Values["currentPage"] = 42;
 
    settingsRoaming.Values["currentBook"] = "Hitchhiker's Guide To The Galaxy";
    settingsRoaming.Values["currentPage"] = 42;
 
    ReadSettings();
}
 
private void ReadSettings()
{
    //If you want typed data when you read it out of settings,
    //you're going to need to know what it is, and cast it.
 
    currentBook = (string)settingsLocal.Values["currentBook"];
    currentPage = (int)settingsRoaming.Values["currentPage"];
            
    DeleteSettings();
}
 
private void DeleteSettings()
{
    settingsLocal.Values.Remove("currentBook");
    settingsLocal.Values.Remove("currentPage");
 
    settingsRoaming.Values.Remove("currentBook");
    settingsRoaming.Values.Remove("currentPage");
}
 

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

In the sample project, you can set breakpoints at the beginning of each method, and then use Visual Studio to inspect our settings values in the Locals tab.

Here is what my data looks like after AddSettings() has executed (click to enlarge):

8-XAML-SettingsAdded

Now, after I’ve read the data from settings and stored it to values in my page:

8-XAML-SettingsRead

Finally, after my DeleteSettings() method has executed:

8-XAML-SettingsDeleted

As you’re building your apps, it’s important to remember that all of this data, both settings and files, are sandboxed to your application.  What this means is that when your application is uninstalled, all of the values are gone with it.  This also means that when you’re building an app that uses these values, and you want to start with a greenfield user experience, you might want to uninstall the app from your machine before testing it to get rid of any legacy values that may have been saved earlier.

In addition to saving these name/value pairs, you might also want to categorize them a bit.  We can create categories of settings, which makes adding and removing groups of settings very simple to do. This can also obviously be done both locally and roaming.  Here’s a look at creating a category, and adding a setting to it:

settingsLocal.CreateContainer("mediaSettings", ApplicationDataCreateDisposition.Always);
settingsLocal.Containers["mediaSettings"].Values["Volume"] = 11;
 

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

So we’ve taken a deep look at Settings, let’s move on to files now.

Local and Roaming Files

Files operate in a very similar way to settings, except that we are actually reading and writing the values to the hard drive, and I’ll demonstrate that in this example as well.  I’ve set up an identical set of methods to the ones we used in the Settings section: an AddFile(), a ReadFile, and a DeleteFile() method.  Here’s a look at the code:

StorageFolder folderLocal;
StorageFolder folderRoaming;
string fileName = "tacotext.txt";
string fileContents = "taco";
        
        
public MainPage()
{
    this.InitializeComponent();
 
    folderLocal = ApplicationData.Current.LocalFolder;
    folderRoaming = ApplicationData.Current.RoamingFolder;
 
    AddFile();
}
 
private async void AddFile()
{
    StorageFile fileLocal = await folderLocal.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
    await FileIO.WriteTextAsync(fileLocal, fileContents + "taco");
 
    ReadFile();
}
 
private async void ReadFile()
{
    StorageFile fileLocal = await folderLocal.GetFileAsync(fileName);
    string textLocal = await FileIO.ReadTextAsync(fileLocal);
 
    fileContents = textLocal;
 
    DeleteFile();
}
 
private async void DeleteFile()
{
    StorageFile fileLocal = await folderLocal.GetFileAsync(fileName);
    await fileLocal.DeleteAsync();
}
 

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

As you can see, the only major difference is that we async/await in our methods that rely on reading the hard drive.  We don’t have to specify folder locations, we don’t even have to define a folder structure if we don’t want to.

In addition, you can look at your files as they are saved.  Each application stores its files locally on the machine, and if you use a breakpoint, you can determine that location on your device.  For instance, the tacotext.txt file that I’ve created is stored at the Path property shown below:

8-XAML-FileStoragePath

Once you’ve created it, you can actually crack the folder open and see the contents, even open the files yourself:

8-XAML-FileLocation

Otherwise, that’s about it!  Saving files, even large files, can be done this way.  You only need to remember the file name that you gave them.  The Windows 8 sandbox takes care of the rest.  Please note that my example above actually only stores a local file, but that you use the EXACT same code (with a reference to ApplicationData.Current.RoamingFolder instead) for Roaming files.

As a reminder, roaming files will not transfer immediately, so don’t expect the type of performance you’ve seen with Skydrive or DropBox’s applications.  Be mindful of the data quota, but otherwise, use this stuff extensively. 

Summary

Settings and Files are a powerful tool in our Windows 8 development arsenal. It’s easy to do, and makes your application so much cooler when it lights up multiple machines at once.  I still tell stories about the first time I played Jetpack Joyride on a second machine, and all of my purchases, jetpacks, equipment, and settings showed up immediately.  It made what is an already awesome game that much better for me.  So much so that I’m recommending you pick it up yourself.  (It’s a free game, but there’s an in-app purchase option for $1.49 to double your coins from each level completed.  Totally worth it.)

8-XAML-JetpackJoyride

To download all of the sample code from this article, click the icon below:

downloadXAML

Tomorrow, we are going to discuss Live Tiles, and how we create both primary and secondary tiles, as well as how we update them.  See you then!

downloadTheTools

12 thoughts on “31 Days of Windows 8 | Day #8: Local and Roaming Data

  1. Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1228

  2. Great article!

    I do have a question about this roaming technology that I can’t test for myself because I have only one Windows 8 device.

    Let’s say I want to write an app that uses a SQLite db as a datastore. I want this db to roam to other devices running the app, so I write it to the Roaming directory…right?

    However, I envision a problem that I can’t workout how to solve: When the app starts up for the first time on device A, the db won’t exist, so it will create a new one and use it. This will get sync’d to the cloud. So far, so good.

    When that app is installed on device B, how does device B know NOT to create a new database file because it should receive one eventually from the cloud as a result of actions on device A? Presumably, if device B did create a new db file, it would overwrite everything device A did as that new file is now the “most recent” version as far as the cloud sync is concerned. If that happened, it would be less than ideal for the user.

    Am I mis-understanding how to use this technology somehow?

    • Yes, I think there’s a misunderstanding. You’re trying to pull off the “poor man’s web service” pattern here. If you have enough data to necessitate a database, you should be using a web service to manage the distribution and synchronization of that data across the user’s machine.

      Roaming storage is meant for files and settings, like their collection of photos, or their chosen background color for the app. It definitely should NOT be used for syncing an entire database. Just my two cents.

      • Ok. Thanks for the feedback.

      • Jeff,
        But what about the case where you might have a very small amount of data, but using a DB to make it all related saves a lot of work. I can imagine a scenario in an app I am thinking about where I might have hundreds or thousands of records (all text) that in total would be WAY smaller then a few dozen pictures.

      • It’s not about the size of the data, it’s about how you use it. You’re trying to use roaming data in a way that is not recommended for several reasons:

        1) Windows controls how often your data is roamed, which means you have no reliability guarantee.

        2) You’re moving the database as a whole, not the individual records. This means that each machine will overwrite the other’s files (because there’s a new timestamp) on the database.

        3) There is no mechanism for “merging.” If a user makes a change in both places, they’re going to lose one of them. That’s not the type of experience you’re looking to provide your user, is it?

        In short, I called this specific scenario out in the DON’T section at the top of my article for a reason. If you are ever making an architectural decision because “it would be easier to do,” you’re making the wrong choice. You only have to build it once, but your user will have to use it thousands of times. A web service is absolutely the route you want to take in this scenario. Every time.

      • I totally get the reasons for not using a database but i am still interested in the scenario

        “Presumably, if device B did create a new file (was db), it would overwrite everything device A did as that new file is now the “most recent” version as far as the cloud sync is concerned.”

        When app starts on a new device does and that device is offline does the roaming folder assume that the file can be created uniquely and let the user create a new one?

        What happens when the app goes online is it a simple case of newest wins?

        Should we deal with it ourselves and check for network connnection and if their isn’t one we store temporarily as alocal folder until network is back?

        TIA

  3. Pingback: 31 Days of Windows 8 | Day #8: Local and Roaming Data

  4. Great article, congratulations…

    I have a question that concerns about the security about files saved to hard drive. There is possibility that user find the file and modify it, am i right?

    Sorry by the bad English.

  5. Pingback: 31 Days of Windows 8 | Day #29: Application Lifecycle « Blankenblog

  6. Pingback: 31 días de Windows 8 | Día 8: Información local e itinerante | La Liga Silverlight

  7. This is a great article.Helped me a lot! Now I have to decided to create a game!
    Thanks.
    I just bookmarked this site🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s