Tag Archives: iphone

Moving an app from Paid to Free

I’ve seen quite a few people saying that it isn’t possible to move an iOS app from paid to being free with an in-app purchase to unlock the full functionality. Fortunately they’re wrong.

“Traditionally” I would have had to remove version one from sale and offer a completely new app, which would have meant that existing users would have to pay again to get the same functionality. Or I’d have to support two apps. Or I’d keep the same app in the store and all existing users would get downgraded to the free version. None of these solutions seemed fair to existing users.

What I wanted was for people who had bought version one to get the full, unlocked version and for new users to be promoted for the paid upgrade.

Since iOS 7 came out in 2013 that it entirely possible. I’ll explain how it’s done here. This isn’t just some theoretical “I’ve seen the documentation” claim – I’ve done it with one of my own apps, Rootn Tootn.

The really short answer: take a look at the session 308 video from WWDC 2013. That’s the only information from Apple that explains how to do it. They have documented the API calls that are required but the actual process is left as an exercise for the interested student. And there are quite a few steps if you want to do it properly.

Firstly you need to get the app receipt. Before iOS 7 this only made sense for IAP but now they are available for all purchases and come in the same format as receipts from the Mac App Store.

Receipts have a number of useful features. In the past they have been used to validate purchase, and they can still be used for this. What’s interesting with the new receipts is that they include both the original purchase and the version number of that original purchase. This means that we can decide whether a user gets the paid functionally by looking for either an in app purchase or a purchase date before a particular time or, more likely, before a particular version.

When you download an app you should get a receipt automatically but you can also use the SKReceiptRefreshRequest class to force one to be generated. (This is also useful during development where, obviously, there is no receipt.)

Once the refresh has completed, you use [NSBundle appStoreReceiptURL:] to access the receipt.

Once you have the receipt the bad news starts.

It’s not in a user friendly format. And Apple do not provide any APIs to read it. Check out Apple’s documentation:

The outermost portion (labeled Receipt in the figure) is a PKCS #7 container, as defined by RFC 2315, with its payload encoded using ASN.1 (Abstract Syntax Notation One), as defined by ITU-T X.690. The payload is composed of a set of receipt attributes. Each receipt attribute contains a type, a version, and a value.

If security is important to you, you should probably write your own code to do this. ASN.1 is a standard format and it’s not that hard.

There are apps that generate the validation code, such as Tighten Pro and Receigen. I can’t vouch for either of them but the reviews seems positive.

There are also Open Source projects that do the same thing. I’ve used RMStore; there’s also VerifyStoreReceiptiOS. The main disadvantage of these is that, as standard, open code it makes it easier for crackers to reverse engineer how you remember that a purchase has been made.

And there you have it. It is possible. It’s just a lot harder than you might imagine. Remember this when someone tells you that it can’t be done.

ShareEverywhere

ShareEverwhere main screen
ShareEverwhere main screen

I was so busy when it came out that I never quite got around to blogging about it here: I have a new app out! It’s called ShareEverywhere. It is built exclusively for iOS 8 and uses the new, built-in “share” functionality, allowing you to share to a good number of services from any app that uses the standard share button.

When I first wrote it, I wasn’t sure how many, if any, developers would build share widgets into their apps. Now that we know the answer is “a lot of them,” I still use ShareEverywhere because it beats having a dozen widgets hiding in your action menu. And there are still services, like Pinboard.in, that don’t have their own native apps.

It’s available now in the App Store for your iPhone or iPad. It costs £1.49, $1.99, €1.79 or your local equivalent.

CameraGPS debrief

As happy as I am with the way that my new app, CameraGPS, a GPS logger application for people who want to geotag their photographs, came out I can’t say that it’s exactly as I envisioned it at the start of the process.

The idea was something like this: many of the GPS logger apps in the App Store require you to either use iTunes file sharing (who connects their iPhone’s to iTunes any more?) or mail yourself the exported document or sign up to some third party fitness or trekking website. Mailing yourself stuff just didn’t feel very slick and I didn’t want to record my trails for fitness purposes.

I felt that there must be a better way. The better way, I thought, was to use iCloud to share the files between iOS and a Mac. This would require writing a recorder app for iPhone and an “exporter” app for the Mac.

It didn’t work out that way in the end. Here’s why.

You see all kinds of horror stories from people who have had to deal with iCloud. Most of those stories come from people who used the Core Data syncing, while there are a few apps that successfully use the document syncing model, not least some of Apple’s own. Using the document storage I figured that, worst case, if something gets screwed up it would only affect a single trail. Using Core Data syncing, an error could mean corrupting the whole database.

So this became the plan: record each GPS trail into a separate document. Because I wanted to “random access” parts of each trail and because I didn’t want to have to load the whole trail into memory all the time, I decided to use Core Data to manage the document. On iOS this is called UIManagedDocument.

The first and most obvious fail here is with UIManagedDocument’s cross platform capabilities. Or rather the lack of them. Unless you count iPhone to iPad as cross platform it just doesn’t work. There is a class on the Mac called NSPersistentDocument that is also a Core Data store and looks on the surface to do exactly the same thing… but it’s in a different format. On iOS it’s a “package” which includes a Core Data store. On the Mac it’s just the store. Mike Abdullah has written an open source component that attempts to be bridge the gap but — as we’ll see shorty — I didn’t get to the point where I was able to test it in anger.

But let’s take a step backwards. Let’s assume that all we need is to move data between iPhone and iPad. Then what?

Well, technically it works — in the sense that documents transfer between machines — just not in a way that would make sense to ship.

To make sense of why it doesn’t work let’s take a quick diversion to look at the format of a UIManagedDocument. As hinted above, it’s not a file. Rather it’s a “package” which a number of files within.

An important file is called DocumentMetadata.plist. This tells you about the rest of the package and is created when the document is. There’s little in here that needs to be changed after document creation. The interesting stuff is all stored in a SQLite file, which is stored in a folder.

When files are stored in iCloud you can’t just use NSFileManager to poke around and see what’s there. Instead you create a query using NSMetadataQuery — which supports both a predicate and a sort descriptor — and listen for updates. Updates come in two parts, the initial state and then changes to that state.

After a few false starts I ended up using KVO to populate a table view showing the current list of documents. This worked nicely, showing the full list, updating automatically when changes arrived over the network.

But, and it’s a big but, what if you want to sort the list. Pretty basic requirement, no?

One of the fun things about NSMetadataQuery is that it can only look for files. Folders are “invisible.”

Most of Apple’s documentation — where there is any — suggests searching for the DocumentMetadata.plist. Which makes sense but with two significant drawbacks: every file that you’re looking for now has exactly the same name; and the modification date is the creation date of the document rather than the time of the last update.

This means that you can’t sort the documents alphabetically or by modification date using only the metadata predicate. To get the names you need to listen for updates, copy them elsewhere and then butcher the file name, that is removing the last component and possibly the file extension.

To get the last modification date you probably need to open the document and do a Core Data query. This sounds pretty straight-forward but, again, there are difficulties. Remember that NSMetadataQuery tells us what’s available in iCloud rather than what has been downloaded onto the current device. Just because the query tells us the file is in iCloud doesn’t mean that you can open it immediately.

UIDocument helps us a bit here: if you ask to open a document it will download it if necessary. However there’s a big difference between opening a local file and downloading one over the network and then opening it. Also remember, at this point we’re not opening it as the result of a user request. We’re just trying to find the last modification date so that we can sort the list of documents conveniently.

None of this, of course, is impossible. It’s just a lot of faff for something that pretty much every app that wants to use UIDocument must have to go through.

Okay, so we get all that done. We’re good to go now, right? Not quite.

Part of the idea of using a UIDocument was that it would appear as a, well, document. But that whole thing about what NSMetatadataQuery “sees” comes back to haunt us.

The short, weird version is that if you configure the application to look for DocumentMetadata.plist, then that’s what is seen in the Settings app. Not very helpful as all the documents will all have the same name.

On the other hand you can configure the app to “know” about the document type. This means that the Settings app understands that they’re document and displays them with the correct “file” name. Unfortunately then the app can’t see the documents properly. Specifically, you can search for the document by name — which is nice — but you can’t actually open it. It’s odd and I’m not entirely sure what goes wrong, but when you call openWithCompletionHandler: it never actually calls the completion handler and the document never becomes available for use. (I initially thought that I just wasn’t being patient enough. I later accidentally left the app running for over two hours and the completion handler still never ran. That convinced me that it wasn’t a patience thing.)

I convinced myself that this couldn’t possibly be how it was supposed to work. My question on Stack Overflow didn’t get any answers and a reply to another, similar question said that the behaviour was “by design.”

I think it’s just broken and that no one is really using it. Either that or there’s a secret handshake that you have to give to the right person at WWDC.

So what’s shipping? Pretty much none of the above. I read about the new iOS 7 methods of Core Data – iCloud syncing and liked what I saw. Additionally iOS 7 comes with a few more APIs with better error handling hooks. This made me happier shipping with Core Data syncing. Just using “raw” Core Data meant that I could simplify a lot of code, use NSFetchedResultController and remove a bunch of the more fragile iCloud code (mostly NSMetadataQuery). While I was quite pleased with the KVO code I wrote, having far less code overall is a much bigger win.

And, in case you were wondering, I also de-scoped the Mac app and integrated Dropbox support instead.

But the bottom line is that this is all a lot harder than it should be. My concerns originally were around error conditions — something that has been very weak in iCloud since it’s introduction in iOS 5 — but it quickly morphed into questions about how it’s all supposed to work at all.

I once quipped on Twitter that you can tell which APIs are the ones Apple uses themselves as they’re the well designed ones. I think it’s pretty clear that Apple don’t use UIManagedDocument.

NSFetchedResultsController and iCloud

This took me a while to figure out so I thought it was worth blogging about. The short version: I’m using Core Data with iCloud syncing and it works… mostly. When starting up for the first time – when there is already data in iCloud –  none of the data appears in a table view, but restarting the app correctly displays it.

I know what you’re thinking: you’re not merging the updates into the right managed object context. Nope. Sorry. Thinking that was the problem is probably why it took me quite so long to track the real problem down!

So, what does the problem look like?

I set up my Core Data stack in the app delegate. Part of that includes connecting to iCloud:

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"XXX.sqlite"];
    NSDictionary *options = @{
                              NSMigratePersistentStoresAutomaticallyOption: @YES,
                              NSInferMappingModelAutomaticallyOption: @YES,
                              NSPersistentStoreUbiquitousContentNameKey : @"XXX"
                              };

    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                   configuration:nil
                                                             URL:storeURL
                                                         options:options
                                                           error:&error]) {

The app delegate passes the managed object context through to the main view controller which then uses it to create a NSFetchedResultsController:

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Edit the entity name as appropriate.
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"XXX" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

    // Set the batch size to a suitable number.
    [fetchRequest setFetchBatchSize:20];

    // Edit the sort key as appropriate.
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"someTimestamp" ascending:NO];
    NSArray *sortDescriptors = @[sortDescriptor];

    [fetchRequest setSortDescriptors:sortDescriptors];

    // Edit the section name key path and cache name if appropriate.
    // nil for section name key path means "no sections".
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                                                                                managedObjectContext:self.managedObjectContext
                                                                                                  sectionNameKeyPath:nil
                                                                                                           cacheName:@"Master"];
    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;

	NSError *error = nil;
	if (![self.fetchedResultsController performFetch:&error]) {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
	    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
	    abort();
	}

(Again, this is pretty much Apple-standard boilerplate code –  nothing clever going on here.)

But what I found was that when there was already data in iCloud, the records did not automatically appear in the main view controller.

Eventually I found that this was not a threading issue – merging the changes into one context and trying to read them from another – by adding an explicit fetch request on a “debug” button. Doing this I could see the new data, even though the fetched result controller could not.

In my app delegate I listened for a number of notifications: NSPersistentStoreDidImportUbiquitousContentChangesNotification and NSPersistentStoreCoordinatorStoresWillChangeNotification. My expectation was that NSPersistentStoreCoordinatorStoresWillChangeNotification would fire before switching from the fallback store to the “real” one and NSPersistentStoreDidImportUbiquitousContentChangesNotification would fire when the new data was available.

I was half right. NSPersistentStoreCoordinatorStoresWillChangeNotification fired on a background thread, so I used GCD to ping it onto the main thread and reset the context.

But NSPersistentStoreDidImportUbiquitousContentChangesNotification didn’t fire at all. I guess the objects didn’t change as such, they just became available, which feels like a slightly false distinction to me.

So my next guess was to fire the fetch request again. The data was visible in the main threads context so surely this would find the data?

Nope. (And don’t call me Shirley.)

I was getting pretty lost at this point so I ended up just semi-randomly stopping the code and looking around in the debugger.

And, long story short, I realised that fetch requests have a reference to the persistent store in them – check out the affectedStores method. This meant that the NSFetchedResultsController was happily, and correctly, reporting on the empty and no longer used fallback store and completely ignoring the new and fully populated iCloud store.

The simple solution was to listen for the NSPersistentStoreCoordinatorStoresDidChangeNotification and create a completely new fetch request.

- (void)storesDidChange:(NSNotification*)notification {
    self.fetchedResultsController = nil;
    [NSFetchedResultsController deleteCacheWithName:@"Master"];
    [self.tableView reloadData];
}

(I did think of just adding the new persistent store to the old fetch request but I wasn’t sure that this would create the refresh anyway and, given the frequency with which this is likely to happen, I thought it would be cleaner just to start from scratch.)

These weird problems almost always boil down to just a couple of lines of code. This time was no exception.

Notes on iOS 7

I’ve been using iOS 7 for a while now — for a couple of months on my iPad and about half that on my iPhone — so thought it was worth a quick summary of my experience. I’m certainly not going into the depth that Arstechnica have; I’ll do it all in a few bullet points.

The good

  • Control Center. Switch on and off BlueTooth and WiFi without having to go into Settings. I’ve been wanting this since iPhone OS 1 so this is more than welcome!
  • The look. It is controversial and it does take a bit of getting used to but overall I like it and it works well
  • I didn’t find the new look to be as jarring as I thought it would be based on what I saw in the screenshots
  • The “Today” view in Notification Center. All your notifications and stuff that’s happening shortly in one place. Felt like nice PowerPoint (Keynote, I suppose) material when I first saw it but I actually find it quite useful
  • The “back swipe” gesture in navigation views that goes back to the previous screen. I noticed how useful it was when I started trying it in apps that don’t support it. Tweetbot I’m looking at you!

The bad

  • Some of the animations look cool the first two times you see them, then they get really annoying as they take too long. Let me tap the frickin’ icons already!
  • Some “old” apps do look pretty bad. Hopefully they’ll get updated soon

The ugly

  • Glitchy, especially the iPad version. Nothing that makes it unusable but lots of little details — such as the keyboard not working on the iPad lock screen sometimes — that makes me a little surprised that they released it when they did
  • As a developer I’m not sure what I think about the auto-update feature for apps. I think I already get pretty high adoption when a new release comes out. However I’ve also had some rough x.0 releases that I’ve been happy have not been adopted super-quickly

Yes, there’s a lot of other stuff but these are the things that I can remember! However, as I said last year, some of the best features are those that blend into the background so much that you forget about them…

Olloclip

This is a long way of saying Thank You to F for the Olloclip, the ideal gadget for someone who loves both their iPhone and photography. Literally only available a couple of weeks before Christmas (for iPhone 5 at least), it still arrived before the 25th.

For those that don’t know, the Olloclip is an attachment for the iPhone’s camera. It looks like this:

IMG_1037

It sits over the iPhone’s camera. It has three lenses on two sides. On one side there’s the fish-eye adapter, on the other is a wide-angle. If you unscrew the the wide-angle it becomes a macro adapter.

The least useful is probably the wide angle. The iPhone camera is fairly wide anyway. Useful maybe but not as dramatic as the other two.

The fish-eye is, perhaps, dramatic but not useful. Something doesn’t have to be useful to be a lot of fun…

IMG_0986

The best of the three is the macro adapter. You have to get it surprisingly close to the subject to make it work. I think this is both useful and dramatic. And still fun.

IMG_1020

The quality is pretty decent too.

IMG_1021

Focusing so close and with a wide aperture, the depth of field is very narrow but this just adds to the drama. Even coffee beans and Cheerios look out of the ordinary.

IMG_1027

And trees look like an alien landscape.

If I had to make one criticism of the Olloclip — and this sounds absurd — it’s too small. I hesitate to put it in my pocket more often because I’m afraid it will fall out, and it’s tricky to use the macro without risking losing the wide angle lens.

Of course it’s difficult to see how else they could do it so this probably says more about my paranoia than anything else.

Overall, it’s great. This is something that’s going to get a lot of use over the coming months I think.