Musings of a PC

Thoughts about Windows, TV and technology in general

Category Archives: Windows Phone Development

Migrating a WP Silverlight app to WP XAML – supporting settings

Earlier this year, Microsoft announced Universal Apps – an enhancement in VS2013 that took advantage of improved API compatibility between Windows 8.1 and Windows Phone 8.1 to allow a much greater amount of code sharing between an app for both platforms.

This article is not going to look at that side of things in any detail. Instead, whilst working on a Universal App version of Relative History, it occurred to me that there might be … challlenges … when it comes to dealing with how a user has been using an existing Windows Phone app. The primary reason why there could be challenges is because a Windows Phone Universal App doesn’t use the Silverlight infrastructure so access to things like application settings get handled in a different way.

Upgrading a published Windows Phone app

There is some guidance published by Microsoft on how to upgrade an app. It mostly focusses on upgrading to Silverlight 8.1, which is not of much help to me, but there is some key information about the upgrade process, namely:

  • If testing, it is necessary to copy the ProductID and PublisherID attributes from the published app into the PhoneProductID and PhonePublisherID attributes in the Package.appxmanifest file. This is for the scenario where the earlier version of the app is already deployed onto a device or emulator but not installed from the Store. It is not possible to upgrade an app installed form the Store by deploying a new version of the app from your development computer.
  • If publishing, you simply submit the app to the Store. Microsoft takes care of replacing the product ID and publisher ID with the values from the app you are updating.

I’ll go into some of the above in more detail in a moment, but the key point here is that you cannot upgrade a Store-installed app in testing. If you want to test the process through the Store, here are the steps (thanks to Romasz for his answer on StackOverflow):

  • Publish a Beta version of the existing app – WP7.0/7.1/8.0 Silverlight.
  • Install that from the Store onto your phone.
  • Submit an update to the Beta version in the Store. Add a new package. Do not replace the old package.
  • Your phone will eventually spot the new version and update it. When you run the updated app, it should then execute any code you’ve written to deal with the upgrade process.

Can settings and files be “upgraded”?

My first thought was: when a user upgrades from a Silverlight-based WP app to a XAML-based WP app, what happens to the settings and what happens to any locally-stored files? Let’s see what the community says …:

I got some great responses to that question:

So, the answer is a qualified yes … so the next question is how do we go about it?

Upgrading an app

If you haven’t already done so, you need to install Windows Phone Power Tools (WPPT). This will help because it allows you to browse and access the contents of the app’s private storage. This is useful, for example, if you want to see what you need to be accessing, updating or (in my case) deleting when you migrate from Silverlight to XAML.

As was touched upon earlier and, as Ginny pointed out, you need to make sure that the App ID remains the same. This is the ID string that the operating system uses to identify your app and can be found in WMAppManifest.xml.

Capture

Copy that string into the clipboard then open the file Package.appxmanifest in VS2013 using the XML(Text) Editor. You need to paste the ID you’ve just copied into the PhoneProductId string in that file and save it.

Capture

The PublisherID can be found towards the end of the same XML section in WMAppManifest.xml and this then gets copied into the PhonePublisherId section of Package.appxmanifest.

Now build your app. If it was building successfully previously, it should still build successfully. You can even deploy it to a device or emulator if you want to double-check :-).

Next step is to install the WP Silverlight version of your app to a device or emulator. Use WPPT to install the XAP file. Once installed, run the app and do whatever you need to do in order to get it set up, or get some data installed … whatever is necessary to properly test the upgrade process.

Updating the settings

Once you’ve installed the Silverlight version and used it a bit, you can use WPPT to look at the Isolated Storage for your app:

Capture

Notice how I’ve expanded the Local directory. The main file we’re interested in now is __ApplicationSettings. Everything else will continue to exist after the app is updated by the WP XAML version of the app, and it then becomes up to you whether or not your new version can use those files, update them or delete them. In the case of my app, for example, the Silverlight version of the app uses the local SQL implementation and the data is stored in .sdf files. With the XAML version, I’ve moved to using SQLite so the .sdf files are essentially useless to me. My app will need to delete them in order to free up the storage space, but I’ll also use the contents of __ApplicationSettings in order to ascertain more information about those databases so that I can re-import the data, this time into a SQLite file.

So … onto __ApplicationSettings. If you use WPPT to get the file onto your computer, you can open it in WordPad and see something like this:

Capture

It is important to note that the application settings stored by a WP Silverlight app cannot be accessed through ApplicationData.LocalSettings. The latter is for Windows Store apps.

To retrieve the settings stored by a WP Silverlight app, we basically have to read and parse the __ApplicationSettings file. A simplistic way to do this would be something like this:

StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
if (localFolder != null)
{
  StorageFile appSettings = await localFolder.GetFileAsync("__ApplicationSettings");
  if (appSettings != null)
  {
    // Got the file - now try to read it
    XDocument oldSettings = new XDocument();
    using (var readStream = await appSettings.OpenStreamForReadAsync() as Stream)
    {
      using (StreamReader sr = new StreamReader(readStream))
      {
        // Dump the first line - it isn't XML
        sr.ReadLine();
        // Then get the rest in
        oldSettings = XDocument.Load(sr);
      }
    }

    XNamespace xns = "http://schemas.microsoft.com/2003/10/Serialization/Arrays";
    XElement f1 = oldSettings.Root.Element(xns + "KeyValueOfstringanyType");
    var f2 = oldSettings.Descendants(xns + "KeyValueOfstringanyType").ToList();
  }
}

That succeeds in reading in the XML. You’ve then got to iterate through the list, looking for Keys that you are interested in and extracting the Values from the corresponding XML node. For example, here is one node retrieved from my settings file:

<KeyValueOfstringanyType xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
  <Key>FirstRun</Key>
  <Value xmlns:d3p1="http://www.w3.org/2001/XMLSchema" i:type="d3p1:boolean" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">false</Value>
</KeyValueOfstringanyType>

Things get a bit more challenging if you used a class or other complex type to save away. Thankfully, Pedro Lamas has written a great bit of code that essentially mimics what the Silverlight code does when it reads in the application settings file. So, now, all you need to do is call GetValuesAsync() and you’ll end up with a set of key pairs including any non-standard classes. In my case, that is an ObservableCollection<DatabaseList>.

Now, I did encounter some struggle points while trying to get Pedro’s code to work in my app. The main challenge was around the handling of the first line in the settings file. That line of text (which I was ignoring in my simplistic approach) actually lists out any “non standard” types so that the DataContractSerializer knows how to parse the XML appropriately and rebuild the objects.

So why was I having problems? Here is the first line from my settings file:

System.Collections.ObjectModel.ObservableCollection`1[[Relative_History.DatabaseList, Relative History, Version=1.4.8.0, Culture=neutral, PublicKeyToken=null]], System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e

The problem I was having was that the call to Type.GetType (in Pedro’s code) was returning null because the system couldn’t match the object. There were a couple of reasons why, in my case:

  1. The WP Silverlight app had an assembly name of “Relative History”. In my Universal App project, I had the Win8.1 project set to an assembly name of “RelHist2.Windows” and the WP8.1 project set to an assembly name of “RelHist2.WindowsPhone”.
  2. The version number expected was 1.4.8.0 but my Universal App has a version number of 1.0.0.0.

One work-around I came up with was to replace the section of Pedro’s code where knownTypes is declared with this:

System.Type[] knownTypes = new Type[] { typeof(System.Collections.ObjectModel.ObservableCollection<Relative_History.DatabaseList>) };

This worked but did mean that the code wasn’t then as flexible as it should be. I finally solved the problem by changing the assembly names in my Universal App to match that of the Silverlight app, and increased the version number to be higher than that of the Silverlight app.

One final tweak to Pedro’s code if you are copying/pasting his code rather than using Cimbalino is to change:

public async Task<IEnumerable<KeyValuePair<string, object>>> GetValuesAsync()

to

public async Task<IDictionary<string, object>> GetValuesAsync()

With all of that done, Pedro’s code then makes it much easier to load in the settings and access the ones you want to retain:

                var oldSettings = await GetValuesAsync();
                // Now see which settings we want to retain
                if (oldSettings.ContainsKey("fred"))
                {
                    var value = oldSettings["fred"];
                }

In the next article, I’m going to be looking at handling licencing when moving from WP Silverlight to WP XAML.