Skip to content

Cocoa Internet Version Checking – check for updates…

It seems that almost every new app you see out there has an on-line version checking system. I remember when the first ones started to appear in Cocoa land (probably from Omni), and I sort of coasted and ignored them. Anyway, this week I was running a Cocoa class, and the question arose. There’s an article on CocoaDevCentral on the subject, but I didn’t like the way it was written – the author starts by assuming that he’d have to write some PHP server side code to handle it, and shows that first, before moving on the the real meat.

Thinking about the problem, we have to be able to uniquely identify our app and recognise its version (that’s easy), and access data over the Internet using a URL. So let’s start with these two.

All Cocoa apps include a Resource called Info.plist; we can edit it directly, or use Xcode inspectors to change values. These include an app identifier, and a version string, which are used for storing user defaults uniquely, and in the standard About panel, respectively. This file is used in all bundles, and we can access the bundle for your application by:

[NSBundle mainBundle]

NSBundle returns bundleIdentifier, which is suggested should be in a unique form like that recommended for Java classes; eg com.plsys.MyApp. It also returns infoDictionary, which is a dictionary of all Info.plist values. The key used to store the version string is CFBundleVersion. So far we have:

NSBundle *app = [NSBundle mainBundle];
NSString *ident = [app bundleIdentifier];
NSDictionary *infoDict = [app infoDictionary];
NSString *myVersion = (NSString *)[infoDict valueForKey:@"CFBundleVersion"];

The next step is to find out how to retrieve our current version information over the Internet. You are going to need a place to store this – any web site that you can update will do: for my example, I just used my .Mac homepage. I need a very small amount of space to hold a text file with the version information, and extra if I want (optionally) to have the user download the latest version of my app from there as well – or from any URL.

Foundation provides a useful class for dealing with Internet content, called NSURL. So we could implement all we need using NSURL, but several other classes make use of NSURL as part of an initialiser. One that is very convenient for us is NSDictionary:

NSDictionary *plist = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:[self versionURL]]];

The NSURL acts as a holder for our URL string, which can supply the contents of that location to NSDictionary’s initialiser. NSDictionary requires a string in a valid plist format, which can be the property list version of XML that Apple have adopted, or the traditional property list format. I find the latter easier to read and sufficient for the purpose. Property lists are composed of expressions, which are C-like values. If these are a ; separated list of assignments enclosed in {}, then the assignments are keys for a dictionary; if they are a comma separated list of values enclosed in (), they represent an array.

A dictionary, keyed by our application identifier, should be sufficient. If the contents of the dictionary are themselves dictionaries, this is a flexible system we could expand for any new ideas we get. Initially, I will use two keys to this dictionary: version, and url. Version will return a version string to compare against the bundle version, and url will be an optional location that will contain the latest version of our application.

NSDictionary *versionDict = (NSDictionary *)[plist valueForKey:ident];
NSString *currentVersion = (NSString *)[versionDict valueForKey:@"version"];
NSString *downloadURL = (NSString *)[versionDict valueForKey:@"url"];

At this point, we have enough information to perform a version check. The only remaining problem to solve is to start a download of the latest version. Again, this is easy to do, using NSWorkspace, which has the ability to open any file/URL for us.

[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:downloadURL]];

That’s all.

My approach to using this code is to create a utility class that can be dropped into all my projects. all it needs is one accessor pair, for the instance variable used above called versionURL, and one action-like method to perform the version check (checkVersion: ). As defined, this class should be instantiated in a nib file so that the action method can be called from the application menu.

Then to use it:

- (void)awakeFromNib {
[versionChecker setVersionURL:@"http:////"];
[versionChecker checkVersion:self];

Finally, the contents of my version checking plist file, which is at the URL above:

com.plsys.TestBed = {
version = "1.1";
url = "http:////";

This must end with a return.

You can download an example application including all the code and the utility class mentioned above.

Post a Comment

Your email is never published nor shared. Required fields are marked *