Tag Archives: ipad

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.

Which Tablet?

I was recently asked to recommend a tablet. I thought my reply might be generally useful, so below is a lightly edited version of what I wrote.

The machine I’d recommend depends. It depends mostly on how much you want to pay and what it might used for. The good news is that, by and large, you get what you pay for. (Corollary: don’t get any of the really cheap ones. Argos, for example, do a really cheap one. Avoid it.)

The main ones I’d consider are:

Kindle Fire HD 7″ £119

By far the cheapest but very much tied to Amazon — indeed it’s pretty much sold at cost with the expectation that you’ll spend more money with Amazon later on. That means there are fewer apps (games), you can’t download/rent movies from iTunes, etc. But if you just want to surf the web, check email, etc. and play some big names games it would be fine. Probably worth spending the extra £10 to get the version without adverts (“special offers”) though.

Google Nexus 7 £199

Nicer hardware than the Kindle but mostly what you get is access to the Google App Store, which has far more apps, lots of which are free or very cheap. It runs Android, which is the main competitor to Apple and is generally considered to be pretty good, though I’ve not used it much myself. It’s also not tied just to Amazon (though you still can’t get iTunes) but you can get most of the Amazon stuff. Like the Amazon one, it’s cheap because Google expect to make money from you in other ways.

Apple iPad Mini £249

Better hardware than either of the previous two (metal rather than plastic case) but, arguably, a worse screen than the Nexus (physically bigger but fewer pixels).

iPad gives you all the iPhone and iPad software — which is typically better than Android. Also gives access to iTunes for music, movies and TV shows. The iPad software is often considered to be bit easier and less confusing than Android and you’d get stuff like FaceTime and iMessage (free text messages with other Apple users) which you can’t get on Android.

iPad mini with Retina display £319

As above but with a far nicer screen and is about four times quicker. It will probably last longer as it’s more future proof (but that’s obviously speculation at this point). Possibly hard to get hold of right now as it literally just came out and it “supply constrained.”

Apple iPad Air £399

As above but with a 10″ screen rather than 8″. I have an older versions of this, though the mini didn’t exist when I got mine…

(The prices above are “retail” prices. Some of the links go to the same product but for a lower price.)

It’s also worth noting that you can get more expensive versions of all of them that come with more space and/or cellular radios (so you can access the Internet when you’re out of the range of a friendly WiFI network).

It’s even harder to give general advice about this than the tablets themselves. In general, the more you want to download movies and large, complex games, the more capacity you’ll need. If you mostly surf the web or read books even the smallest versions should be okay. (Indeed, that’s what I use.)

The 3G/4G question is tricky. Me, I get the cellular radio because I do travel with my iPad and I have a Pay As You GO SIM which means I don’t pay a penny in months that I don’t use it. But it does cost more. You might prefer to spend the extra to get a larger storage capacity.

When I first got an iPad, it was because users of one of my apps were asking for a version that used the iPad’s bigger screen. I was skeptical that I would actually use it. These days I probably use it more than my Mac. I guess what I’m saying is that it’s worth getting the right product rather than just the cheapest.

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…

iOS 6

Like all the best upgrades, iOS 6 is almost entirely invisible. It works just like iOS 5 — which is to say, pretty well most of the time — but with some convenient new additions. Also, unlike version five, it’s been relatively stable throughout the beta process.

What’s new and what will you like? I’ve grown so accustomed to most of them that I had to look up the “What’s new” page on Apple’s website. Really, that’s a good thing. Invisibility is the fate of a feature that’s quickly integrated with how you use a device. (The thing that makes it tricky is that it’s also the fate of a completely useless feature that you never use.)

Roughly ordered by how much I like them:

  • Do not disturb. This is the one feature that the Blackberry had that I missed in iOS. I only had a Bold for a few months but being able to switch off “work” between certain hours was brilliant, and the same is true of Apple’s implementation. You set a range of hours and it automatically mutes. This feature alone is worth updating for
  • Shared Photo Stream. This will be even better when I can share directly with other people’s devices, but being able to create a web gallery of Photo Stream pictures has already proved to be useful
  • iCloud tabs. This feature sat idle until Mountain Lion and then… great. The only thing I would ask is for this to work on my iPad!
  • New options when receiving a phone call. The “decline but remind me later” is a great option

Stuff I’m ambivalent about:

  • Maps. Apple switched from using Google’s maps to their own. I miss Street View but otherwise all the functionality seems to be there. Searching for stuff seems a bit hit and miss, just like with Google Maps. Search results differ but I’ve not used it enough to determine whether one is better than the other
  • Siri. I have an iPhone 4 and an iPad 1, the former of which doesn’t support Siri, the latter of which doesn’t support iOS 6
  • Passbook. This might be great when there are apps to support it. Right now, a waste of a spot on my home screen
  • FaceTime over 3G. Not sure if it will be supported in the UK. Rarely had the need to call when there’s been no WiFi

The only “bad” I can think of is that it doesn’t run at all on my two year old first generation iPad. I understand technically why they made this decision, but at the same time it’s a little galling to have such a relatively recent device be declared obsolete.

In conclusion, this is one of Apple’s “refinement” releases. They seem to have a big update (iOS5, Lion) and then a release that fixes some of the rough edges (iOS6, Mountain Lion). 2012 has been a year of two spit and polish releases. I’m not complaining.

Instapaper versus Pocket

Instapaper

  • The save page bookmark works with Google Reader. That is, it saves the link I’m reading rather than the Google Reader web page.
  • Page turn. Rather than display the page as a long, scrollable single page, Instapaper paginates the document. You can “turn” the page much as you do in iBook or the Kindle app.
  • Progress indicators. I like that it shows roughly how long a document is and how much has been read.
  • Clean, minimal interface.
  • The trick of reading the current page to make site like NYTimes (with page view restrictions) or LWN (pay walls) is a great idea but not completely reliable.
  • Recent updates have been less stable. Going from day to night mode — an otherwise nice idea — almost always crashes. Putting the app into the background often loses the current location (it returns to the end of an archived article).

Pocket

  • Brighter, prettier interface. Much nicer to use than Read It Later, at least the last version that I used.
  • Less app support.
  • Syncing articles is (subjectively) faster.
  • Stable.

Despite the longer list in favour of Instapaper I’ve been using Pocket more in the last couple of weeks. It’s the last bullet in both lists that’s the killer. The paywall-swerve, expensive fonts and even the Google Reader cleverness are all well and good, but if the app isn’t ready when I want to read it’s all for nothing.

Glitches

It started with this image. Or rather the glitch that you can see in the middle of the screen.

It’s one of the screens in a new app that I’ve been developing. There’s lots of hard stuff in there but this is the first problem that has really stumped me. What it appeared to be was this: a text view (UITextView) on a table (UITableView) showed a nasty glitch when you tried to place the curser in the text box.

First thoughts:

  • Transparency
  • Scrollview (UITextView) within a scrollview (UITableView)
  • Image background

So I switched off the transparency. (No effect.) I switched off scrolling while editing. (No effect.) I made the background boring. (No effect.)

I was stumped.

I showed it to some guys at the London iOS Developer Group meeting. They too were stumped. (This was a bit of a relief. It meant that I wasn’t doing anything completely stupid, which is always a risk.)

So I tried writing a question for Stack Overflow. And, as I often do, I stumbled across the solution.

Note that the text view has a small border and rounded corners? Yup, that’s the culprit. Or at least, some of that code is. Here are the two lines that do it:

self.layer.cornerRadius = 5;
self.layer.shadowOpacity = 0.5;

(There were quite a few other lines adjusting the layer too, which is what took the time to narrow down.)

It was nothing to do with the table view or the text view per se. It wasn’t even any of the visible transparency effects.

Of course, in the screen I have no need for a shadow, indeed you can’t even see one, so the solution was easy. Quite why this combination causes such a weird effect have no idea, but I’m glad that I found the solution.

(I raised a bug report with Apple: rdar://11041657.)