4. Updates
You can update the Windows Phone Store catalog information for your
app at any time. This includes the descriptions, artwork, keywords,
pricing and regional availability, and so on. Although this goes
through the same ingestion pipeline as a full submission, if you do not
update the XAP, the time-consuming steps of validating and testing the
XAP are skipped, so this kind of update is relatively quick. You can
also upload a new version of your XAP at any time, and in all cases,
you use the same submission form and upload mechanism. Just as with a
full submission, you can check the progress of an update in Windows
Phone Store through the portal.
When you perform an incremental build of your app in Visual Studio
and run it in the emulator or on a device, the fresh XAP replaces the
previous version. However, it does not replace any data stored in
isolated storage. On the other hand, if you perform a
clean-and-rebuild, this does wipe out isolated storage. This is because
a clean-and-rebuild performs a complete uninstall/reinstall, as opposed
to an update. In the Windows Phone Store, when you submit an update,
this behaves the same way as an incremental build: it replaces the XAP,
but does not touch isolated storage. The only way that isolated storage
is automatically cleaned out is when the user uninstalls the app.
So,
when you publish an update to your app, you might want to clean out
isolated storage yourself upon the first run of the updated app.
Alternatively, if you want to keep the old data, you might need to make
allowances for any changes in data schema that you have made. For
example, you might want to convert all the old data to the new format
upon first run. Be aware that this only applies to data stored in
isolated storage files; it does not apply to app state or page state,
which are not persisted across runs of the app.
Getting updates right is not always straightforward, particularly if
you accommodate users skipping an update (for example, the user goes
from version 1 to version 4 without installing versions 2 or 3). To
thoroughly test update permutations, one strategy is that for every
released version of the app, you generate a representative snapshot of
data in the app (that is, perform downloads, save data, and so on) and
then use the Isolated Storage Explorer tool from the SDK to copy this
data off the device and archive it with the version of your app (in a
folder, or if you’re using source control, put it in there). Then,
whenever you update your app, you can deploy the app, update the
isolated storage with the archived copy for each previous version’s
files, and see what happens.
The system checks for updates for installed apps periodically; thus,
there little reason for the developer to perform this check
independently. Nonetheless, if you do want proactively to check for
updates from within your app, you have a couple of options.
The platform includes the MarketplaceDetailTask, MarketplaceHubTask, MarketplaceReviewTask and MarketplaceSearchTask
classes, which all represent Launchers that access the Windows Phone
Store for a set of specific operations. Each one brings up a Windows
Phone Store UI page for a specific task. There is no public Windows
Phone Store API with which you can fetch data programmatically, without
showing UI.
With that said, it is fairly easy to construct web requests to the
Windows Phone Store to fetch metadata for a given app, assuming that
you know what you’re looking for. That might not be as simple as it
sounds, however, because some of the values in your WMAppManifest.xml will be modified during Windows Phone Store ingestion. This includes the Author, Publisher, Capabilities, and—crucially—your app’s ProductID.
Figure 13 shows a simple implementation of the key techniques in this approach (the StoreInfo
solution in the sample code). This app fetches its page in the Windows
Phone Store and compares the version number there with the current
version number. If the Windows Phone Store version is higher, the app
then launches the MarketplaceDetailTask
so that the user can examine the information for the update. This is a
fairly crude approach which involves mining the app’s HTML page for
interesting data, but this page will contain most of the Windows Phone
Store data for your app, so it’s a good way to retrieve all this data
very simplistically.
The app constructs a simple HttpWebRequest for its own page in the Windows Phone Store. This is in the format “http://www.windowsphone.com/s?appid=GUID”, where GUID is a placeholder for the app’s ProductId.
private const string storeUrl = "http://www.windowsphone.com/s?appid=";
private const string appId = "3375b957-7d3e-4330-8bb7-3e0c28765432";
private void getData_Click(object sender, RoutedEventArgs e)
{
HttpWebRequest webRequest =
(HttpWebRequest)HttpWebRequest.Create(new Uri(storeUrl + appId, UriKind.Absolute));
webRequest.BeginGetResponse(httpRequestCallback, webRequest);
}
This will return an HTML page for the app (assuming the ProductId
is actually found in the Windows Phone Store). The app first gets the
version of the currently-executing app. There are various ways you can
achieve this, and one of the simplest is to retrieve the AssemblyName:
of course, this assumes that your assembly version is the same as the
version you specify in the app manifest. Next, the app retrieves the
page HTML from the callback result and parses it for the version string.
HTML parsing is an arcane art: some developers prefer to do this via
regular expressions, but this is an inherently fragile approach.
Instead, there are plenty of third-party libraries to support this. An
even simpler approach is to treat the HTML as a flat string, and simply
scan it for the version substring. In this case, the string is in this
format: “<span itemprop=\“softwareVersion\”>1.0.0.0</span>”, where 1.0.0.0
represents the version number. Keep in mind, however, that this could
change, and a more sophisticated solution would be more resilient to
such changes.
private const string versionElement = "<span itemprop=\"softwareVersion\">";
private const string spanEnd = "</span>";
private void httpRequestCallback(IAsyncResult result)
{
AssemblyName assemblyName = new AssemblyName(Assembly.GetExecutingAssembly().FullName);
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
using (WebResponse response = request.EndGetResponse(result))
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
string htmlString = reader.ReadToEnd();
int start = htmlString.IndexOf(versionElement) + versionElement.Length;
int end = htmlString.IndexOf(spanEnd, start);
if (start >= 0 && end > start)
{
string versionString = htmlString.Substring(start, end - start);
Version storeVersion = null;
Dispatcher.BeginInvoke(() =>
{
if (Version.TryParse(versionString, out storeVersion))
{
current.Text = assemblyName.Version.ToString();
store.Text = storeVersion.ToString();
}
if (storeVersion > assemblyName.Version)
{
getUpdate.IsEnabled = true;
}
});
}
}
}
}
If the Windows Phone Store version is greater than the current version, the app enables the button to launch the MarketplaceDetailsTask so that the user can see the details for the update. If you don’t provide the app’s ProductId as the ContentIdentifer
for the task, the task defaults to fetching the details page for the
calling app. Obviously, for the first version of your app, in the early
stages of development, your app will not be in the Windows Phone Store,
so you can use an alternative ProductId for a published Windows Phone Store app for testing purposes in this scenario.
private void getUpdate_Click(object sender, RoutedEventArgs e)
{
MarketplaceDetailTask storeTask = new MarketplaceDetailTask();
storeTask.ContentIdentifier = appId;
storeTask.ContentType = MarketplaceContentType.Applications;
storeTask.Show();
}