Apple TV (4th gen)

Keeping with tradition, I’m going to write about my new gadgets but my “unique selling point” is going to be brevity. And, in this case, another angle I bring is being a  Brit — much of the mainstream tech press is American and content is still very much a local.


  • The same but better. I liked the old one, so I mean that as a compliment.
  • Feels much faster than the old one. (The hardware should mean that it does, but if Android has taught us anything it’s that specs don’t guarantee performance.)
  • think Siri is going to be great, and the more apps that it understands the better. For the past couple of weeks1 it’s only supported iTunes and some of the less useful stuff (weather, sports scores).
  • I really like the remote. Clearly it’s not designed for serious gamers but I don’t consider that a flaw. I’m not a serious gamer. (As an aside, it’s weird that much of the press have been complaining about the remote as a game controller and saying that the device won’t replace your Xbox One or PS4. I think it’s a good compromise between the two extremes.)


  • I think the price is starting to get a bit high as a casual/streaming device.
  • Missing a bunch of apps, though, hopefully, this is something that will be solved with time. There’s already Netflix, but I’d like to see BBC iPlayer 2, 4oD and Amazon video. US media companies seem to have been more on the ball than those in the UK, though many had apps on the old Apple TV; maybe they had less work to do to bring their streaming apps to the 4th gen?
  • I don’t have many apps yet but it’s clear that we’ll need folders sooner rather then later.


  • The idea of copying all your credentials and configuration over from your iPhone is fantastic. Entering your Apple ID username and password using the on-screen keyboard is a pain. However, it seems not to be working for a lot of people and for me it took so long that I almost gave up.

Overall it’s already slightly better than the 3rd generation Apple TV and has the potential to get a lot better as the App Store fills out. In my mind, it’s odds of success likely rest on the cost. It’s therefore a shame that it’s possibly on the high side. Of course, that’s not been a problem for Apple in its recent history.

  1. I got the developer kit. []
  2. Having said that, I’m successfully using Auntie Player. []

Starting with CloudKit Syncing

One of the slightly more hidden features of CloudKit, Apple’s cloud-based back-end service for applications, is that you can use it to synchronise content as well as simply query it. I use this approach to sync favourite search terms between devices in my app Yummy.

However, I found the process wasn’t as well documented as it might be. It’s all there, but it’s written as many man pages are: it makes total sense if you already know what you’re looking for. This post is my attempt to make the process clearer.

We’ll start by inserting some data into the cloud data store and then try to get it back out again.

Pushing data to CloudKit is pretty straight forward. As we’ll see later there’s a little more preparation required in practice, but at it’s simplest it looks like this:

// Get a reference to the private cloud database
CKDatabase* db = [[CKContainer defaultContainer] privateCloudDatabase];
// create a new record
CKRecord* myRecord = [[CKRecord alloc] initWithRecordType:@“Record” zoneID:zoneID];
// set the interesting values
[myRecord setObject:@“Data” forKey:@“Key”];
[db saveRecord:myRecord completionHandler:^(CKRecord* record, NSError* error) {
  // error handling
  // save a local copy in cache

We’ll come back to the zoneID variable used in the second line.

It’s asynchronous and only takes a couple of API calls. Nice.

Once data has been pushed to the cloud you have to get it back again. In my case there are few enough records that I could just download everything and do a local “diff.” That would look like this:

// create a query
CKQuery* query = [[CKQuery alloc] initWithRecordType:@“record”
                                              predicate:[NSPredicate predicateWithValue:YES]];
// define what to do with the records to be returned
CKQueryOperation* queryOperation = [[CKQueryOperation alloc] initWithQuery:query];
queryOperation.recordFetchedBlock = ^(CKRecord* record) {
  // do something with each record
// fire off the query
[db addOperation:queryOperation];

Doing the local diff is left as an exercise for the reader.

Again, just a few lines of code. (Quick aside: I’m not sure if it’s a good thing that the block is called for every record individually. It feels like it might be easier given a block of records, but CloudKit is generally consistent about this one record at a time approach.)

But that feels like it’s missing the point. CloudKit supports just getting a list of what’s changed since the last sync.

The class to find the records that have changed since the last sync is CKFetchRecordChangesOperation, but if we put that in a method:

CKFetchRecordChangesOperation* changesOperation = [[CKFetchRecordChangesOperation alloc] initWithRecordZoneID:[CKZoneID defaultZone]
[[[CKContainer defaultContainer] privateCloudDatabase] addOperation:changesOperation];

(The nil token means we’re starting from scratch — get us everything. Next time we sync, we pass in the token given at the end of the last sync.)

However if we run it we get an error:

<CKError 0x7fd326282bb0: “Server Rejected Request” (15/2027); server message = “AppDefaultZone does not support sync semantics”; uuid = 00138311-1714-4651-BE36-9D85B3CB5D51; container ID = “containerID”>

So, in short, the default configuration does not allow us to sync! We have to create a new record zone to store our records. A corollary of this is that you can only sync private stores, since you cannot create new zones for the public cloud database.

In principle this is quite simple but in practice it can get a little hairy as every call to CloudKit is asynchronous and there’s lots of error handling to consider.

I’m not going to show all my working (because what I have is pretty hacky and I’m sure you can do better) but the algorithm is:

  1. Are we logged into iCloud? If not, give up now
  2. Do we have a record of the current zone ID? If so, jump to step 7
  3. Fetch the list of zones that we can use
  4. Loop through the list returned. If there’s one we recognise, save the reference and jump to step 7
  5. Create a new zone
  6. Save the Zone ID in the completion handler
  7. Now get on with our sync / insert / update / delete operation

Note that queries tend to pick the right zone automatically, so if you’re searching rather than syncing you don’t need to worry about this.

And that’s it. Syncing is possible, just a little convoluted.

Photography, opinions and other random ramblings by Stephen Darlington