Saturday 13 October 2012

Free To Paid iPhone Application Data Migrations With Custom URL Handlers


Free stores user data in an encrypted SQLite Database. Since we really wanted a way to copy the data file from Free to paid we developed a fairly clever solution: use a registered URL handler as a means of communication between application versions. This is how it works:
  1. A customer downloads  free and enters some unique private data. All information is written to a database in the application’s document directory.
  2. When the 10 record limit is hit free asks them to upgrade. An upgrade button launches to explain the process and allow the user to download the paid version of the product.
  3. Once Strip is purchased and installed, our customer will launch free and click an “Export to free” button. free reads the application database into an NSData object, Base64 encodes it and then Launches a “strip://” URL with the encoded file contents as a URL parameter.
  4. The full version of paid is registered as a handler for “strip://” URLs. The OS launches paid and passed the URL to the handleOpenURL method of the application delegate. It parses and decodes the data and writes the database to the desired location in the new applications Document folder.
  5. Our customer now has access to all the data the originally entered into free!

Project Setup

This tutorial assumes that your project is already configured with two targets, one for your full version, and one for the Lite.
Start by creating a new InfoLite.plist file for your Lite version. We use 2 separate .plist files to ensure that the Full version is the only registered URL handler (we don’t want the Lite version responding to upgrade events itself!). It is easiest to just make a copy of the existing Info.plist file named InfoLite.plist.
Next, Open up the Build preferences for the Lite target. Change the Info.plist File property to read InfoLite.plist. This will ensure that the Lite target includes the proper file.
Now open the Info.plist (used by the Full Version) and register a custom URL handler. This is a straightforward process of adding elements as shown in the following screenshot.
Please note that the URL identifier and URL scheme must be unique for your application — see this stepwise overview of the process for reference.
Finally, the application must include code to perform Base64 encoding itself. This functionality is, unfortunately, not directly accessible in the framework SDK. There are a few options in Cocoa, including using OpenSSL’s libcrypto, but the easiest method is to use the encoding libraries fromGoogle Toolbox for Mac which is conveniently distributed under the Open Source Apache License. Download GTMDefines.h, GTMBase64.h and GTMBase64.m, then add them to your project. The GTM code compiles perfectly on the iPhone and even includes web safe variants that are immediately suitable for use in URLs without percent encoding.

he controller action code will read the data file, encode it and then format a URL with the protocol prefix / URL scheme configured in the previous step.
#import "GTMBase64.h" 
...
NSData *fileData = [NSData dataWithContentsOfFile:@"/path/to/LiteApplication/Documents/file"];
NSString *encodedString = [GTMBase64 stringByWebSafeEncodingData:fileData padded:YES];
NSString *urlString = [NSString stringWithFormat:@"myapp://localhost/importDatabase?%@", encodedString];
NSURL *openURL = [NSURL URLWithString:urlString];
[[UIApplication sharedApplication] openURL:openURL];

The Full Version

The final step is to make your application handle the Open URL event and reverse the encoding process.
Add a handleOpenURL method to the AppDelegate implementation as follows. This will receive the URL, sanity check it, and then parse the query string directly. The resulting binary data is written to the file in the full application’s data directory.
#import "GTMBase64.h" 
...
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
    if([@"/importDatabase" isEqual:[url path]]) {
        NSString *query = [url query];
        NSString *importUrlData = [GTMBase64 webSafeDecodeString:query];

        // NOTE: In practice you will want to prompt the user to confirm before you overwrite their files!
        [importUrlData writeToFile:@"/path/to/FullApplication/Documents/file" atomically:YES];
        return YES;
    }
    return NO; 
}

No comments:

Post a Comment