Building a Windows Phone 8.1 app, log 03

Data persistence decisions

Quick Recap

(These blogs are probably best read in order, or at least catch the TL;DR section at the end of each to get some context. Here’s log 02, and that has a link its predecessor)

I’m building a Time-Tracker app that will let me keep tabs on how much time I’m spending with my hobbies and typical leisure activities. So far I’ve talked about the initial setup. For context, here is an image of what the app looks like:

Most recent layout (slightly newer than blog entry, but shouldn’t make much difference to article content)

Decisions, decisions.

After getting an extremely basic UI in place with Pivot controls and simple buttons, it was time to start persisting data so I could begin dogfooding my app. Here were some of the persistence decisions I had to take:

  1. Whether to use a simple database or a file structure.
  2. What fields I should persist.
  3. How will data migration be handled when the time comes for it.
  4. Should I remain hardcore about only persisting data within my model classes or should I blur the boundary and persist some of the data within my ViewModel classes as well? I suppose that this would be obvious to an experienced developer but I wrestled with the idea for a little while. The reason the struggle even existed is that the concept of ‘days’ did not really exist within my model, but was vital to the visual arrangement of things and hence a part of the ViewModel.

Database vs File based approach

Frankly, I really wanted to go with a database approach because my data is quite easy to model relationally, I wouldn’t have to worry about a bunch of file operations, bulk operations would be trivial and, well, I also wanted to take LINQ for a spin. I was under the impression that the WIndows Phone platform has a built-in SQL-Lite implementation and was quite sure things would be smooth and awesome. Turns out I was wrong. SQL-Lite is a 3rd party add-on…and unfortunately I couldn’t get the extension to work on my system after an hour or so of struggling. I gave up, leaving this space to mature a little before I come back to it, and decided to the file structure route.

Some file based decisions

I was bummed that the DB route didn’t work out, because I now had to decide on things like: if I should use a binary file or a text based format and whether I should go with a serialization library or create my own method for reading and writing out the data. Since a binary format would be unreadable, it would make debugging just a little bit more difficult and this being my first time writing such an app I wanted to be able to read the file content easily — so the decision to go with a text-based file was easy.

At this point we’ve narrowed things down to using a readable text file based solution. More experimentation and internet searching ensued as I looked for serialization libraries. I played around with a few options most required more infrastructure setup than I was in the mood for (learning more about the interfaces involved and supplying the right attributes to various classes/properties etc), so I decided on XmlSerializer which worked reasonably well right out of the box (it’s part of the .Net platform, no add-ons).

Finally, my persistence setup is that I have one file for every day of data stored; the file name being the date the data is for. Inside the file there is an XML tree and that stores details for activities of the day. Here is what the file looks like:

<?xml version=”1.0" encoding=”utf-8"?>
<ArrayOfActivityInfo xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd=”http://www.w3.org/2001/XMLSchema"> <ActivityInfo> <ActivityName>Working out</ActivityName> <HoursSpent>2</HoursSpent> <ActivityDate>2014-08-02T00:00:00-07:00</ActivityDate> </ActivityInfo> <ActivityInfo> <ActivityName>General reading</ActivityName> <HoursSpent>1</HoursSpent> <ActivityDate>2014-08-02T00:00:00-07:00</ActivityDate> </ActivityInfo> <ActivityInfo> <ActivityName>TV</ActivityName> <HoursSpent>0</HoursSpent> <ActivityDate>2014-08-02T00:00:00-07:00</ActivityDate> </ActivityInfo> <ActivityInfo> <ActivityName>Video Games</ActivityName> <HoursSpent>3</HoursSpent> <ActivityDate>2014-08-02T00:00:00-07:00</ActivityDate> </ActivityInfo>
</ArrayOfActivityInfo>

As you can tell, the data is extremely simple, but very flexible in terms of what it would consider valid. I don’t expect a person’s activities to change too rapidly, but even if they did the data file wouldn’t need to be migrated. The list of activities a person sees on the page would be stored in a settings file separately from this data and that can be updated as needed. For reporting (when we get to it), I am planning to simply crawl through all data files and create an in-memory dictionary of activity-time spent pairs. The data files don’t have to be symmetric, if day 1 you engaged in activities A, B and C, and day 2 you did A, C and D; the final list will be A, B, C and D with time-spent calculated across both (outer join).

Some other considerations

There are still other considerations like when I should write to the file and what would happen to days that the user did not input any data for. The latter was simple…no data entry = no data file. I’ll deal with that when I have to. For the first question, I could have done the file-writes on page navigation or pivot item’s selection changed event, but instead of introducing more state-checking complexity to the code, I decided to write to the file every time the user changes any of the values. I’m not certain if this would be too many file-writes, but I’m guessing they’re not. The files are tiny and writing to them should be a very rapid affair. If this turns out to be a perf disadvantage I’ll change the logic later.

Also, you might find some shortcomings with the current format, for instance I’m only storing hours which seems like too coarse of a unit; that is intentional. I want to practice writing ‘data migration’ code and I will need to make changes to this schema before shipping. As a result, instead of agonizing over the perfect schema now, I’ll write robust migration code and update the schema as I go along.

TL;DR

  • Couldn’t get SQL-Lite to work ☹ ☹ ☹
  • Using local file storage for persistence. Easily accessed through platform namespace Windows.Storage.
  • Using XmlSerializer for converting objects into XML. Using the very basic canned settings, nothing fancy.
  • Storing one file for each day. Simple, dumb but effective.
  • File is written every time the user changes any value. Perf remains to be seen, will alter as needed.
  • Not agonizing over the perfect schema now. Will invest in robust data migration code and keep updating the schema as I go along.

Email me when Vinayak publishes or recommends stories