/ C#

Windows Phone - Save data on app deactivation

So here's a scenario which I think is pretty common. You have an app, in the app the user does some stuff that generates data that you would like to store inbetween the app sessions.
How?

IsolatedStorageSettings approach

My first thought was to use the IsolatedStorageSettings beacause it's easy to use, needs little code and flexible (you can just send in an object)

However there was two problems with this approach, the first was that the settings can only hold data that is one "layer" deep. So A class works fine, a list aswell. But if it's a class that includes lists or other classes, then all those lists and classes will be null. Not ideal...

The other problem is that I actually want to save data... Not settings, it technically "works".
But this is not the intended way and it's not as pretty as it could be. And I like pretty!

IsolatedStorageFolder approach

There's another set of classes available for me in the IsolatedStorage API, those classes deal with "real" files in my apps sandbox.

But it's a little bit more complicated, because now I actually have to deal with files. For starters the file API doesn't accept raw objects as input. That is easily fixed with this line that uses the awesome library Newtonsoft.JSON
string data = JsonConvert.SerializeObject(objectToSave);

One think to think about when using this though is that the object you're Serializing can't have any circular dependencies. I had a SolidColorBrush in my object which apparently got such a circular dependency. This will lead to a StackOverflowException.

Alright so we got a string that we want to save, let's try the naïve approach (my teacher keeps on saying that you should always try the naïve approach first, it could be good enough)

The naïve approach

This time I tried to do a simple save the same way I would if I saved anything in app.

byte[] fileBytes = System.Text.Encoding.UTF8.GetBytes(data);
StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder;
var file = await local.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
        
Stream writeStream = await file.OpenStreamForWriteAsync();
using (StreamWriter writer = new StreamWriter(writeStream))
{
	await writer.WriteAsync(data);
}

There is however a problem with this...
You see, when using await the program doesnt really wait for it to complete, instead it schedules it to come back to that place later when the "awaited" command is done.
So in my understanding what happens is that when we hit the await keyword, the task is started and a callback set up so the program can continue when it returns. And then the program continues with the next thing after the await call, in my case that is nothing. And when the deactivated method returns the program will be termintated...

Not really what I had in mind.

The more clever approach

Alright!
So using await didn't really work out as expected, but some of the functions I use only come in Async versions... Luckily there's a fix for that, our trusty friend Wait() is here to save the day!

So, by simply waiting for every async operation I can force the program to stop and wait (don't want to do this with UI running) and only return when my method is really done.

byte[] fileBytes = System.Text.Encoding.UTF8.GetBytes(data);
StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder;
var file = local.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting).AsTask().Result;

Stream writeStream = file.OpenStreamForWriteAsync().Result;
using (StreamWriter writer = new StreamWriter(writeStream))
{
	writer.WriteAsync(data).Wait();
}

And it turns out that this works really well!

So now my Deactivated method looks something like this:

private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
	if (App.DataToSave == null)
    	return;

	System.Diagnostics.Debug.WriteLine("Initialize save process!");
    IsolatedStorageHelper.SaveFile("clientData", App.DataToSave).Wait();
    System.Diagnostics.Debug.WriteLine("FILE SAVED SUCCESSFULLY!");
}