As I Learn CloudKit Syncing - Part 5: Tracking Game Changes

This is part 5 in a series of posts talking through how I built CloudKit syncing into Qiktionary

In the last installment of this series I discussed how I was tracking newly created games.

Tracking changes to a game that has already been synced with CloudKit has to be approached in a different way.

The first approach I thought about using was based on time. I would track the “last updated” times of all the games and also track the last sync time. Then I could just query for all games with a more recent “last updated” value.

I decided against that approach for a couple of reasons. I was worried I wouldn’t be able to correctly track the last synced date when things went wrong with a sync, thus possibly letting some game updates slip through the cracks. And I also wanted to be a good CloudKit citizen and only send values that had actually changed since the last sync, and not the entire game every time there was a change.

The approach I’m using

I decided to create a table called GameChange that stores the gameID and a set of changedFields that correspond to column names on the Game table. When a game is updated I create a GameChange record with the set of fields that have changed on the game. If there is already a GameChange record for that game, I update the GameChange’s changedFields value with any new fields that have changed on that game.

This makes it really easy to know what games have changed, all I have to do is SELECT * FROM GameChange. Then, after those changes have been sent to CloudKit using the CKModifyRecordsOperation1, I can just delete the GameChange record.

Don’t track changes that don’t need to be synced

It took me a bit to really figure this out but it may seem pretty obvious to you: only track changes to a game that need to be sent to CloudKit. For example, I ignore changes when:

  • Updating the remoteRecordID field.
  • Updating a game with new data that was fetched from CloudKit.

I do this by having two different “save” methods on my Game class, save and saveWithoutTrackingChanges. The save method is inherited from FCModel, and it looks a bit like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (BOOL)save
{
    NSArray *changedFieldNames = [self changedFieldNamesWithoutRemoteColumns];

    BOOL existsInDB = self.existsInDatabase;
    BOOL generateChanges = !self.stopSaveChangeGeneration;

    BOOL saved = [super save];

    if (saved && generateChanges && existsInDB) {

        BOOL hasChanges = changedFieldNames.count > 0;

        if (hasChanges) {
            [GameChange saveChangeForGame:self withChangedFields:changedFieldNames];
        }

        return saved;

    }else{
        return saved;
    }
}

Note the call to changedFieldNamesWithoutRemoteColumns which returns an array of field names that have changed on the game record, filtering out the remoteRecordID column that we don’t need to sync with iCloud.

saveWithoutTrackingChanges just sets the stopSaveChangeGeneration flag around a call to save:

1
2
3
4
5
6
7
8
9
10
- (BOOL)saveWithoutTrackingChanges
{
    self.stopSaveChangeGeneration = YES;

    BOOL result = [self save];

    self.stopSaveChangeGeneration = NO;

    return result;
}

The other important piece here is the call to [GameChange saveChangeForGame:self withChangedFields:changedFieldNames], which is implemented like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
+ (instancetype)saveChangeForGame:(Game *)game withChangedFields:(NSArray *)changedFields;
{
    GameChange *gameChange = [GameChange firstInstanceWhere:@"gameID = ?", game.gameID];

    if (!gameChange) {
        gameChange = [GameChange new];
        gameChange.gameID = game.gameID;
        gameChange.changedFields = [NSSet setWithArray:changedFields];
    }else{
        NSSet *existingChangedFields = gameChange.changedFields;
        NSMutableSet *mutableChangedFields = [NSMutableSet setWithSet:existingChangedFields];
        [mutableChangedFields addObjectsFromArray:changedFields];
        gameChange.changedFields = mutableChangedFields.copy;
    }

    if ([gameChange save]) {
        return gameChange;
    }else{
        return nil;
    }
}

Bonus: tracking game deletions

I take a similar approach to tracking when a local game is deleted. I have a GameDeletion table that stores the CKRecordID of any game that has been deleted (and has already been synced to iCloud). I don’t keep the local Game record in the database either (with something like a deleted BOOL to mark it as deleted). The Game record is really deleted from the database, along with it’s corresponding RemoteRecord entry.

When I’m setting up my CKModifyRecordsOperation, all I have to do is SELECT * FROM GameDeletion. Then after the operation succeeds, I can delete the GameDeletion record.

Tracking complete

If you recall the syncing recipe is as follows:

  1. Track local changes
  2. Send changes to the server
  3. Resolve conflicts
  4. Fetch server changes with CKFetchRecordChangesOperation
  5. Apply server changes
  6. Save server change token

Now that I’ve tracked all the local changes I need to track, it’ll finally be time to start sending those changes to the server. That I will start covering in my next post.


  1. I’ll eventually get to how I’m using the CKModifyRecordsOperation to send all these local changes to CloudKit.

As I Learn CloudKit Syncing - Part 4: Tracking Local Changes

I’m syncing games. Game data can change in three ways: A new game, a change to an existing game, and a deleted game. All three are tracked differently.

Tracking new games

I can know if a game is new based on whether it is already linked to a CKRecord. According to the docs, the way you link local data to a CKRecord is through encoding the CKRecord’s metadata with the -encodeSystemFieldsWithCoder method on CKRecord.

At first I was saving the encoded metadata along with the game data in the Game table. It was a BLOB column1 called remoteRecord. FCModel will store BLOB data as an instance of NSData. Here is how I turned the CKRecord into NSData to store in the Game table:

1
2
3
4
5
6
7
8
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];

[instanceValue encodeSystemFieldsWithCoder:archiver];

[archiver finishEncoding];

NSData *remoteRecordData = [NSData dataWithData:data];

And here is how I converted it back from an instance of NSData to a CKRecord:

1
2
3
4
NSData *data = (NSData *)databaseValue;
NSKeyedUnarchiver *coder = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
CKRecord *record = [[CKRecord alloc] initWithCoder:coder];
[coder finishDecoding];

This worked well until I implemented the “Fetch server changes with CKFetchRecordChangesOperation” step of the CloudKit Syncing recipe2.

In this step, at some point I need to query the Game table and lookup a specific game, and all I have is a CKRecord instance. I can’t search the remoteRecord BLOB column for the CKRecord because it’s been encoded into a bag of bytes.

I also can’t query the Game table by the gameID (that’s the Game table’s primary key column) which is stored in the CKRecord. Even though the gameID is one of the Game properties I’m saving on the CKRecord. Why?

It has to do with how CKFetchRecordChangesOperation works3. As its name implies, it only pulls in changes. So for example if a game’s record on iCloud has its guesses value changed, then the CKRecord returned by CKFetchRecordChangesOperation will only include the new value for guesses but not any of the other values on that CKRecord.

So the way I’ve worked around this is to create a new table called RemoteRecord that has two fields, a remoteRecordID and a remoteRecord. Now the CKRecord instance is encoded into the remoteRecord column of RemoteRecord, and the local Game table has a remoteRecordID foreign key relationship to the RemoteRecord table.

The thing that makes it possible to query a local Game from a CKRecord is what I’m using as the remoteRecordID. Every CKRecord has a CKRecordID associated with it, and that CKRecordID has a property called recordName which is a string identifier that doesn’t exceed 255 characters. By using the recordName as the value of the remoteRecordID, I can now easily lookup a local Game from a CKRecord, kind of like this:

1
2
3
NSString *recordName = record.recordID.recordName;

Game *existingGame = [Game firstInstanceWhere:@"remoteRecordID = ?", recordName];

I did consider just putting the remoteRecordID column on the Game table and not creating the RemoteRecord table at all, but storing the CKRecord in a seperate table from Game made some things easier when tracking changes to a local game.

So to bring this back around to how I’m tracking new local games, with this setup, all I have to do is query for Games that don’t have a remoteRecordID set, like this:

1
NSArray *newLocalGames = [Game instancesWhere:@"remoteRecordID IS NULL"];

In the next installment I’m going to cover how I’m tracking local changes to existing games.

Get notified of new posts by following me on Twitter.


  1. I’m using SQLite for my local database, wrapped by FMDB and FCModel

  2. As a reminder about what that recipe is, read Part 1

  3. Definitely going to be digging into this more later.

As I Learn CloudKit Syncing - Part 3: Operational Approach

There are two sides to the CloudKit API: convienence and operations. The convienence API is there to quickly get up and running with CRUD, meant to work on single items like saving a CKRecord or fetching one. Even though the convienence API can make it easy to get started quickly, it can get messy:

Implementing CloudKit syncing using the “Tiny Data, All Devices” strategy as I wrote earlier necessitates the use of the other side of the CloudKit API: operations. More specifically, NSOperation and NSOperationQueue.

So in preperation for this feature, I did some homework. I read the Concurrency Programming Guide, the NSOperation reference, and the NSOperationQueue reference.

I also watched Advanced NSOperations talk from this years WWDC and my mind was opened to the power of NSOperation1.

If you haven’t watched that talk yet, please stop reading now and watch it, and then download the sample code2.

Advanced Operations and CKRecordZone

The key part of the CloudKit service that makes this syncing solution possible is the use of a custom CKRecordZone. A CKRecordZone object “defines an area for organizing related records in a database”. The public and private databases each have a default zone, and custom zones can be created as needed. Custom zones have two extra bits of functionality that are crucial for syncing: atomic commits and the ability to fetch only the changes that have occurred since the last fetch.

So I’m going to need to create a CKRecordZone before I can sync the game data to that zone. But before I can create a CKRecordZone I need to make sure I have permission to access iCloud. I could use the convienence API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[self.container accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError *error) {

    if (accountStatus == CKAccountStatusAvailable) {

        CKRecordZone *newZone = [[CKRecordZone alloc] initWithZoneName:@"SyncZone"];

        [self.container.privateCloudDatabase saveRecordZone:newZone completionHandler:^(CKRecordZone *zone, NSError *zoneError) {

            if (!zoneError) {
                // Start syncing
            }

        }];
    }
}];

This… smells. It works until things start going wrong and those errors stop being nil. Being able to gracefully recover from errors, as the CloudKit team has said, isn’t the difference between a good app and a great app. It’s the difference between a non-functioning app and a functioning one.

So I think I can do better. And that “better” comes from using the operation API and some of the techniques found in the Advanced NSOperations talk.

Operation Dependencies

An operation is a discrete unit-of-work. Using NSOperation dependencies, you can make sure an operation only performs work if another operation finishes successfully.

The high level dependency graph of the above code looks a bit like this:

 ┌────────────────┐ 
 │                │ 
 │ Account Status │ 
 │                │ 
 └────────────────┘ 
          ▲         
          │         
          │         
┌──────────────────┐
│                  │
│Create Record Zone│
│                  │
└──────────────────┘
          ▲         
          │         
          │         
 ┌────────────────┐ 
 │                │ 
 │      Sync      │ 
 │                │ 
 └────────────────┘ 

Sync will only execute if Create Record Zone is successful. Create Record Zone will only execute if the Account Status check is a success.

This smells better. In the Create Record Zone operation, if CloudKit returns an error, I’ll have a chance to recover. For example, if I get back the CKErrorRequestRateLimited error, I can grab the CKErrorRetryAfterKey value from the error’s userInfo dictionary and schedule an attempt to retry the operation. If the retry is successful, the Create Record Zone operation completes and the Sync operation is then executed.

So that’s the high-level overview of my approach to building a gracefully recovering CloudKit syncing solution. In the next post I’m going to cover more specifics of the implementation.

Be notified of new posts by following me on Twitter.


  1. And I’m not the only one

  2. The first version of the sample code has a few bugs in it but Dave DeLong is working on a fix

AILCKS - Part 2: Keeping Things Simple

I’m building sync for a game called Qiktionary, which is a simple (yet hard) word game. Discover the secret word by guessing other words and using deductive logic. Basically Word Mastermind except at the end of a game of Qiktionary you learn an interesting fact related to that word1.

So the data model is composed of stuff like words, games, guesses, etc. There is also word packs which is just a way for us to logically group words and provide progress in a more interesting way. They look like this:

All this data is stored locally on the device in a SQLite database. I’m using FCModel to simplify the database access as I like the clean and small abstraction over FMDB. The app ships with a seed database that includes all the word packs and words (and a few other things) that is copied on first launch. If that doesn’t work the app also ships with a large plist file that has the word and word pack structured data and the app can import from the plist into the database if need be.

This basic setup works pretty well. As you can see, there is no server involved. Everything is stored locally. When that above list of word packs need to be rendered there is no asynchronous network request to fetch the them, just a simple SELECT * FROM WordPack2.

I like simple.

Introducing syncing makes this not so simple, which makes me nervous. CloudKit is supposed to be easy, but just because something is easy doesn’t mean it can’t yield a good deal of complexity.

So my goal is to try and keep the complexity to a minimum. To accomplish that I have to do the absolute bare amount of changes to the app as I can. That means I’m only going to sync what I have to.

I could put the words and the word packs in the public iCloud database and use CloudKit to fetch them when needed, but that would change the assumption that the data is just a synchronous database-fetch away. There would be benefits to putting them in iCloud, like the ability to update them without an app release. Maybe in a later release.

I could also build out the Game Center leaderboard and achievement features3 alongside sync but instead I’m doing this CloudKit integration as its own release and so those features will have to wait until sync is implemented (and works reliably).

What I am going to do is:

  • Push device Game data to iCloud.
  • Pull Game data from iCloud and put it on the device.
  • Associate Game data with a Game Center player

Hopefully that is simple enough that even I can’t screw it up.

Watch out for Part 3 where I’m probably going to screw it up.

Be notified of new posts by following me on Twitter.


  1. The facts are courtesy of the QI Elves, a group of devishly smart people who write all the funny stuff on the QI show hosted by Stephen Fry. They also have a very funny podcast that you should definitely check out.

  2. Okay, it is a bit more complicated than that but still it’s synchronous.

  3. Remember from Part 0 that leaderboards and achievements were the impetus to getting sync implemented in the first place

As I Learn CloudKit Syncing - Part 1: Asking Questions

I described in the first part of this series, I’m adopting the “Little Data, Lots of Clients” CloudKit syncing strategy for syncing Qiktionary game data.

Both the Advanced CloudKit talk from 2014 and the CloudKit Tips and Tricks talk from this year cover how to use CloudKit in this way, but in a very high-level way:

  • Use a custom CKRecordZone in the user’s private database.
  • Custom zones support atomic modifications (only commit the changes if all changes can occur)
  • Custom zones also support fetching a “diff” of changes that have occurred since the last sync using the CKFetchRecordChangesOperation class
  • Custom zones allow you to create a CKSubscription that will notify clients of any changes to a zone, allowing the client to fetch the changes with the aforementioned CKFetchRecordChangesOperation
  • CKRecord has support for serializing only the metadata fields for storage alongside the local record using - encodeSystemFieldsWithCoder:.
  • CKRecord’s can be submitted back up to iCloud with just the metadata fields and fields that have changed. Which means you don’t have to fetch an entire CKRecord before updating it.

This seems to be the basic machinery useful in building CloudKit syncing. In Advanced CloudKit, they also layout a recipe to follow when performing a sync:

  1. Track local changes
  2. Send changes to the server
  3. Resolve conflicts
  4. Fetch server changes with CKFetchRecordChangesOperation
  5. Apply server changes
  6. Save server change token

One of the main strengths of CloudKit is how un-opinionated it is. As just a transport mechanism, it’s very flexible in how you structure your application and architecture. What this means though is that there is a lot more left to figure out on your own. At this point in the journey, here are all the things I’m left to figure out:

  • How do I track local changes? Should I hook into sqlite and listen for all changes made to the tables I’m syncing and create some sort of “changes” table. What does that table look like?
  • When do I perform a sync? At app launch? Every 60 seconds? Whenever anything changes? When the app is backgrounded?
  • What about when a record is deleted? I can’t just delete it from the local database anymore? Maybe that can go into the “changes” database too?
  • How do I deal with conflict errors when sending modifications to the server? Actually, the fact that CloudKit is making me answer this question is great, I should be forced to figure this one out. Core Data syncing suffered because it hid too much of this from the programmer.
  • What if iCloud is down? Or the device has a bad connection? Or the server is busy? Or iCloud rate limits me? Or there is no iCloud account? Or the user switches iCloud accounts? Or I’m sending too much data?
  • Since Qiktionary is already out, what kind of problems will I run into migrating people’s data when they first open the app after Qiktionary w/syncing is released? What if Qiktionary tries to do the initial import on both a person’s iPhone and iPad at the same time?
  • A whole other class of questions related to architecture and writing clean code. What’s the best way to go about that?
  • Also, for Qiktionary, this all has to corresponding to a Game Center account. So the local games are all syncing to iCloud under a certain GC account. If the user switches to a different GC account, the games don’t go along with them. This means they could be playing on multiple GC accounts on a single iCloud account. Question: How?
  • Zone Subscriptions are another concern. They aren’t 100% necessary but they should help to answer at least half of the “when do I sync” question. But they come with their own questions, like: when do I ask the user for permission to send them push notifications for this purpose? Is it a little silly to say hey I need your permission for push notifications because: sync?
  • How do I do this all in a way that doesn’t break other things, like duplicating games or lost games, overwriting games, scores, and achievements?

Since I am a couple of days into this now, I think I have a couple of these questions nailed down (I’ve even written some code so a few of those answers have survived that test), but many are still open questions, which I will trying to answer as this work (and this series) continues for the next couple of weeks.

Stay tuned for Part 2. Get notified of new posts by following me on Twitter. In the meantime download Qiktionary and learn some cool facts :).

As I Learn CloudKit Syncing - Part 0

We released Qiktionary a little over a month ago. It’s a playful word game that allows players to unlock interesting facts by solving a mastermindish word puzzle. There are two modes: single player (the main game) and multiplayer where you can challenge your friends on Game Center to a round of three games. Qiktionary works on both iPhone and iPad.

Since it works on both the iPhone and the iPad, we considered the possibility of adding syncing. We knew some people would play on both their iPhone and iPad and would want their games to sync to all their devices. Multiplayer is built on top of Game Center matchmaking so we already have syncing there taken care of; but single player data is all just stored locally in a sqlite database.

I punted on syncing until a week ago when I started implementing Game Center leaderboards and achievements for the single player game. Without syncing, the following scenario is likely to happen to people that play on both an iPhone and an iPad on the same Game Center account:

  • Play many games on their iPhone, earning achievements that unlock prizes and rocket them up the leaderboard. The sqlite database on their iPhone diligently holds the records for all these games played. Game Center stores the info about their leaderboard scores and achievements
  • Put down their iPhone and pick up their iPad, which doesn’t know anything about all those games hidden away on their iPhone
  • Qiktionary on the iPad shows them all these achievements they’ve earned even though locally they haven’t played a single game.
  • Play a game on their iPad. Qiktionary updates their score on the leaderboard based on that single game. Player rockets back down the leaderboard. Qiktionary also attempted to award them the “played your first game” achievement even though they’ve already played many games on their iPhone.
  • Player is sad.
  • Player leaves bad review on App Store.
  • Qiktionary is sad.

So it seems to me that there is no way of getting around implementing syncing when doing Game Center leaderboards/achievements for a univeral iOS game.

Which brings me to CloudKit.

I don’t have a lot of time to build in syncing. I’m not going to be running my own servers. There isn’t a ton of data to sync (if a player completely finishes Qiktionary they will have only generated at most 10MB). Qiktionary for now is iOS only and even if we do expand to other platforms I’m okay with the new CloudKit REST and JS APIs being sufficient to support that move. CloudKit also has nice support for this type of syncing strategy (which they call tiny data, all devices).

So: CloudKit. This is what I’m using and I’ve been learning all I can about it the last couple of days. I’ve watched all the WWDC sessions (some multiple times). Introducing CloudKit and Advanced CloudKit from last year and CloudKit Tips and Tricks from this year were especially helpful. The Advanced NSOperations talk from this year has also given me a strategy for working with CloudKit operations that I think I am happy with (I’ll cover this in a future post). There isn’t a ton of Sample Code but I’ve learned some from the CloudKit Catalog and the live JS version is pretty helpful for quickly getting a feel for the APIs. I’ve also learned a lot by looking at the source for the newly released CKSIncrementalStore.

There are a decent amount of CloudKit tutorials out there but I’ve found most of them are focused on the Big Data, Tiny Phone strategy of CloudKit. This is the strategy used by things like iCloud Photos.

Which is why I am writing this series (shamefully copying David Smith’s As I Learn watchOS series). I’ve just started implementing sync but I am going to try and talk through some of the problems I run into and figure out which things work and which things don’t.

Stay tuned for Part 1 where I will be talking through the “Tiny Data, All Devices” strategy of CloudKit.

Be notified of new posts by following me on Twitter. In the meantime download Qiktionary and learn some cool facts :).

Interactive Animated Transitions on iOS

I closed my talk at UIKonf by talking about a technique to build better animated transitions on iOS by making them “always interactive”. The user should be able to interrupt an animated transition with a gesture, cancelling it any time they want. By default animated transitions will wrap the entire transition in [UIApplication beginIgnoringInteractionEvents], effectively blocking all interaction during the duration of the transition. It’s treated almost like a rotation event, which exhibits the same blocking behavior.

It should work like this:

The technique I’ve been using is to just turn an animated transition into an interactive transition.

Interactive transitions are supported by the transition system, it’s just that they usually start with a continuous gesture, like a pinch or a screen edge pan. That gesture kicks off the transition (be it a navigation controller push/pop or a modal present/dismiss), and then once the gesture ends, the relevant views are animated into place (or off screen, whichever makes sense for that specific transition).

And since interactive transitions don’t block interaction (of course), then we should be able to use them to make always interactive animated transitions.

The only difference is that the interactive transition starts with an animation, not a gesture.

At a high level, the difference between an normal interactive transition and an interactive animated transition looks like this:

Of course it’s a bit more complicated than the above illustration, and I touched on some of that complexity in my talk. The rest of this post will be a more detailed examination of how to implement these strange hybrid transitions.

Converting the Animated Transition

If you already have an animated transition, you’ve had to implement these two methods of the transitioning delegate:

1
2
3
4
5
6
7
- (id<UIViewControllerAnimatedTransitioning>)
    animationControllerForPresentedController:(UIViewController *)presented
                         presentingController:(UIViewController *)presenting
                             sourceController:(UIViewController *)source;

- (id<UIViewControllerAnimatedTransitioning>)
    animationControllerForDismissedController:(UIViewController *)dismissed;

The object you return from these methods conform to the UIViewControllerAnimatedTransitioning protocol, and are responsible for defining exactly how an animated transition works by implementing the transitionDuration: and animateTransition: methods. The transition system will call these methods after you’ve kicked off a transition, and you can return nil to tell the system to just use it’s (the transition system’s) default transition.

To convert an animated transition into an interactive one you will also need to implement the “interaction” equivalent of these methods:

1
2
3
4
5
- (id<UIViewControllerInteractiveTransitioning>)
    interactionControllerForPresentation:(id<UIViewControllerAnimatedTransitioning>)animator;

- (id<UIViewControllerInteractiveTransitioning>)
    interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator

If these methods aren’t implemented (or you return nil) then the transition system falls back to the animated transition and blocks interaction during the transition, which we want to avoid.

To turn on interactivity then all we need to do is implement these methods and return an object that conforms to the UIViewControllerInteractiveTransitioning protocol, which only has one required method: startInteractiveTransition:.

So let’s say we already have an animated transition that looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@interface CustomAnimatedTransition : NSObject <UIViewControllerAnimatedTransitioning>
@property (assign, nonatomic) BOOL reversed;
@end

@implementation CustomAnimatedTransition

- (instancetype)init {
    if (self = [super init]){
        _reversed = NO;
    }

    return self;
}

- (NSTimeInterval)transitionDuration:(id)context {
    return 0.35;
}

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)context {
    UIView *toView = [context
                        viewControllerForKey:UITransitionContextToViewControllerKey].view;
    UIView *fromView = [context
                        viewControllerForKey:UITransitionContextFromViewControllerKey].view;
    UIView *container = [context containerView];

    NSTimeInterval duration = [self transitionDuration:context];

    if (!self.reversed) {
        toView.bounds = CGRectMake(0, 0, 280, 180);
        toView.center = CGPointMake(container.center.x, -90);

        [UIView animateWithDuration:duration animations:^{
            toView.center = container.center;
        } completion:^(BOOL finished){
            [context completeTransition:YES];
        }];
    } else {
        [UIView animateWithDuration:duration animations:^{
            fromView.center = CGRectMake(0, 0, 280, 180);
        } completion:^(BOOL finished){
            [context completeTransition:YES];
        }];
    }

}

@end

And our transitioning delegate looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (id<UIViewControllerAnimatedTransitioning>)
    animationControllerForPresentedController:(UIViewController *)presented
                         presentingController:(UIViewController *)presenting
                             sourceController:(UIViewController *)source {

    return CustomAnimatedTransition.new;
}

- (id<UIViewControllerAnimatedTransitioning>)
    animationControllerForDismissedController:(UIViewController *)dismissed {

    return ({
        CustomAnimatedTransition *transition = CustomAnimatedTransition.new;
        transition.reversed = YES;

        transition;
    });

}

Notice how we implement both “sides” of the modal transition by using that reversed property. This is a fairly common technique since it’s a good practice to have symmetrical transitions (where each side of the transition is a mirror of the other).

To make it an interactive transition all we have to do is make this class conform to the UIViewControllerInteractiveTransitioning protocol and move our animation code from animateTransition: into startInteractiveTransition:, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@interface CustomAnimatedTransition : NSObject
                            <UIViewControllerInteractiveTransitioning,
                                UIViewControllerAnimatedTransitioning>

@property (assign, nonatomic) BOOL reversed;
@end

@implementation CustomAnimatedTransition

- (instancetype)init {
    if (self = [super init]){
        _reversed = NO;
    }

    return self;
}

- (NSTimeInterval)transitionDuration:(id)context {
    return 0.35;
}

- (void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)context {
    UIView *toView = [context
                        viewControllerForKey:UITransitionContextToViewControllerKey].view;
    UIView *fromView = [context
                        viewControllerForKey:UITransitionContextFromViewControllerKey].view;
    UIView *container = [context containerView];

    NSTimeInterval duration = [self transitionDuration:context];

    if (!self.reversed) {
        toView.bounds = CGRectMake(0, 0, 280, 180);
        toView.center = CGPointMake(container.center.x, -90);

        [UIView animateWithDuration:duration animations:^{
            toView.center = container.center;
        } completion:^(BOOL finished){

            [context finishInteractiveTransition];
            [context completeTransition:YES];
        }];
    } else {
        [UIView animateWithDuration:duration animations:^{
            fromView.center = CGRectMake(0, 0, 280, 180);
        } completion:^(BOOL finished){

            [context finishInteractiveTransition];
            [context completeTransition:YES];
        }];
    }
}


- (void)animateTransition:(id<UIViewControllerContextTransitioning>)context {

}

@end

The only other change in the startInteractiveTransition: method is the call to finishInteractiveTransition in the completion of the animation.

Then, in the transitioning delegate, we can return the animator object passed into the interactive delegate methods (casting it to tell the compiler that this object conforms to the required UIViewControllerInteractiveTransitioning protocol):

1
2
3
4
5
6
7
8
9
10
11
- (id<UIViewControllerInteractiveTransitioning>)
    interactionControllerForPresentation:(id<UIViewControllerAnimatedTransitioning>)animator {

    return (id<UIViewControllerInteractiveTransitioning>)animator;
}

- (id<UIViewControllerInteractiveTransitioning>)
    interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator {

    return (id<UIViewControllerInteractiveTransitioning>)animator;
}

At this point we have a working transition with an animation, but instead of blocking all interaction, keeps the door open for a gesture to take control of the transition at any time. Before adding a gesture recognizer to handle that case, we first need to fix a bug that will occur if we were to try and use this code in a navigation controller transition. You can see that bug in action here:

The navbar crossfade animation is delayed until after the animation finishes, instead of happening at the same time. Compare to how it should look:

Fixing the navbar bug

The transition system tries its hardest to animate the navbar along with a custom transition. With an animated transition, it knows the duration of the animation and so just tells the navbar animate your crossfade using a duration of X seconds. But with an interactive transition, there is no duration (the user could pan for an arbitrary amount of time).

If there is no duration, then how could the navbar animation possibly work? Interactive transitions have a concept known as completion percentage. This is a number between 0.0 and 1.0 that is supposed to signify how far along the interactive transition is in it’s own conception of “far along”. For example, if the interactive transition was driven be a pan gesture, from left to right, then the further to the right the user moves their pan, the further along the transition is and the closer to 1.0 it’s completion percentage becomes.

The transition’s completion percentage is updated by calling the updateInteractiveTransition: method on the context, passing in a value between 0.0 and 1.0.

The transition can use this completion percentage value to manually drive the navbar animation by using features of the CAMediaTiming protocol. David Rönnqvist has a great writeup on implementing this technique in his Controlling Animation Timing post.

The transition system sets the navbar layer’s speed to 0, effectively pausing it’s animation, and then uses the completion percentage to set the timeOffset of the layer to “scrub” through the animation.

Knowing that we need to update the completion percentage to fix the navbar bug, how do we go about doing that for our interactive animated transition, especially during the initial animation?

The best way I’ve found to do this is to use a CADisplayLink, which is like an NSTimer but synchronized to the refresh rate of the display. This class is usually used to drive a custom animation system (see this objc.io article for an example) but we are instead going to use it to calculate the completion percentage during the initial animation based on the animation’s duration, using this formula:

percentComplete = elapsedTime / animationDuration;

This should run only during the initial animation so we’ll setup the display link right before the animation code, like so:

1
2
3
4
self.startingTime = CACurrentMediaTime();

self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];

After adding it to the mainRunLoop, the display link will start calling the tick: method 60 times per second, but before that we record the CACurrentMediaTime() which we’ll use to calculate the elapsedTime. The tick: method looks like this:

1
2
3
4
5
6
7
8
9
- (void)tick:(CADisplayLink *)link
{
    NSTimeInterval elapedTime = link.timestamp - self.startingTime;
    NSTimeInterval duration = 0.35;

    CGFloat percentComplete = MIN(1.0, elapedTime / duration);

    [self.context updateInteractiveTransition:percentComplete];
}

Using the display link’s current timestamp and that startingTime we stashed away we can calculate the elapsed time (in seconds) and then calculate the completion percentage (capping it at 1.0).

When the animation finishes, the display link needs to be stopped (so it doesn’t continue calling tick: until the end of time) so we need to invalidate it, like so:

1
2
3
4
5
6
7
8
9
[UIView animateWithDuration:duration animations:^{
            toView.center = container.center;
        } completion:^(BOOL finished){

            [self.displayLink invalidate];

            [context finishInteractiveTransition];
            [context completeTransition:YES];
        }];

Introducing Interaction

We’ve built the foundation for an interactive animated transition so now we can add the actual interaction bits. For this example, I’m going to cover how to use a pan gesture recognizer to create this interaction effect:

During the presentation or dismissal of the modal view controller the user can interrupt the transition by panning inside the modal view, deciding either to finish or cancel the transition.

If you’ve ever tried to add interaction to a view that is animating you know that it doesn’t work the same as a view at rest. That’s because of the Model vs Presentation problem. A lot has been written about this problem and how to deal with it (including the aforementioned article on interactive animations in objc.io).

The gist of the problem is that during an animation, the values of the properties being animated aren’t their current values as shown on screen, but instead are the values those properties will become once the animation reaches completion. An illustration of the problem:

So the view only ever thinks it’s in either the beginning location (before the animation has begun) or the ending location (once the animation finishes), but never any point inbetween while the animation is running. This is why a hit test on a view in the middle of an animation doesn’t work as you’d expect.

Fortunately, you can get access to a view’s presentation properties, which does reflect the property values as they are currently seen on screen, using the presentation property on the view’s layer:

To add our pan gesture recognizer and have it only recognize when the pan happens inside of our modal view, we need to do a little extra work. First, we create it and add it to the transition’s container view, setting our CustomAnimatedTransition object as the target:

1
2
3
4
5
6
UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc]
                                    initWithTarget:self
                                            action:@selector(didPan:)];

[container addGestureRecognizer:gesture];
gesture.delegate = self;

This will match any pan inside the container view, so we have our CustomAnimatedTransition object also become the delegate of the gesture and implement the method - (BOOL)gestureRecognizerShouldBegin:, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gesture;
{
    CGPoint location = [gesture locationInView:self.context.containerView];

    CALayer *presentationLayer = self.view.layer.presentationLayer;

    if ([presentationLayer hitTest:location])
    {
        return YES;
    }else{
        return NO;
    }
}

This method gives us the ability to decide whether a the gesture should begin before that recognizer’s handler method is called. We use the location of the touch in the container view to do a hit test on the view’s presentation layer, indicating that the gesture system should proceed if the touch is on the presentation layer.

Note Not shown above are the definitions of the two properties context and view on the CustomAnimatedTransition class. context is the parameter passed into startInteractiveTransition: saved to a weak property for use in the gesture recognizer delegate and target/action methods. view is either the “from” or “to” view, depending on whether this is a pop/push, or presenting/dismiss. In either case, view always belongs to the view controller on top of the transition. For example, in a dismiss transition, view would be the “from” view controller’s view.

Before we move on to implementing the gesture handler we need one more tweak to our existing code to get it to work. We need to ensure that our animation allows user interaction by including the UIViewAnimationOptionAllowUserInteraction option, like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[UIView animateWithDuration:duration
                      delay:0
                    options:UIViewAnimationOptionAllowUserInteraction
                    animations:^{

            toView.center = container.center;

        } completion:^(BOOL finished) {
            [gesture.view removeGestureRecognizer:gesture];
            [self.displayLink invalidate];

            [context finishInteractiveTransition];
            [context completeTransition:YES];
        }];

Notice also that we remove the gesture once the animation has finished.

The Gesture Begins

Time to implement the gesture handler didPan: method. Since we are working with a continuous gesture, didPan: will get called multiple times and we will be performing different actions depending on the gesture’s state. Our first job is to handle the initial state, UIGestureRecognizerStateBegan and cancel all animations, allowing the user’s gesture to take over control of the transition and the position of the view.

1
2
3
4
5
6
- (void)didPan:(UIPanGestureRecognizer *)gesture {

    if (gesture.state == UIGestureRecognizerStateBegan) {
        [self.view.layer removeAllAnimations];
    }
}

As soon as the animations are removed from the presented/dismisses/pushed/popped view like the code above, then the view will jump to it’s final location, not merely stopping in it’s tracks in the current location. This is because of that Model vs Presentation problem I described above: once those animations are no longer driving the presentation, the view assumes the position of it’s model layer, which as we now know is the view’s final position1.

So to prevent the jumping behavior, we again have to use the presentation layer. This time we use the position of the presentation layer to set the position2 of the model layer before removing the animations:

1
2
3
4
5
6
7
8
9
10
11
- (void)didPan:(UIPanGestureRecognizer *)gesture {

    if (gesture.state == UIGestureRecognizerStateBegan) {
        CALayer *layer = self.view.layer.presentationLayer;
        self.view.layer.position = layer.position;

        [self.view.layer removeAllAnimations];

        self.initialViewCenter = self.view.center;
    }
}

Note We’re stashing off the view’s center CGPoint to use later as the gesture changes

Have you ever wondered what that BOOL finished parameter in animation completion blocks was useful for? Well wonder no more because there is finally a purpose for it! When animations are removed using removeAllAnimations, the animation completion block will be called and the finished param will be set to NO. We need to use this to conditionally complete the transition or our gesture and transition will be short-circuited as soon as we remove the animations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[UIView animateWithDuration:duration
                      delay:0
                    options:UIViewAnimationOptionAllowUserInteraction
                    animations:^{

            toView.center = container.center;

        } completion:^(BOOL finished) {
            [self.displayLink invalidate];

            if (finished) {
                [gesture.view removeGestureRecognizer:gesture];

                [context finishInteractiveTransition];
                [context completeTransition:YES];
            }
        }];

We can invalidate the display link either way because it was only for driving the completion percentage while the initial animation was running.

The Gesture Changes

We want the view to follow the users touch as they pan around the screen. As the location of the pan gesture changes the didPan: method will be called and the gesture’s state will be UIGestureRecognizerStateChanged. We’ll use a fairly common technique to achieve this effect:

1
2
3
4
5
6
7
8
9
if (gesture.state == UIGestureRecognizerStateChanged) {
    CGPoint translation = [gesture translationInView:gesture.view];

    CGPoint centerTranslated = self.initialViewCenter;
    centerTranslated.x += translation.x;
    centerTranslated.y += translation.y;

    self.view.center = centerTranslated;
}

translationInView: returns a point that represents how much the location of the pan has changed since the start of the gesture, so all we have to do is combine the initialViewCenter we stashed away at the beginning of the gesture with the translation to set the view’s new center position.

It might be tempting to save yourself from creating the initialViewCenter property and just resetting the translation back to CGPointZero after every change, but don’t do it. Resetting the translation to zero will muck with the finishing velocity of the gesture, which we’ll need to create a smooth continous finishing animation. As the docs for setTranslation:inView: pretty clearly state: Changing the translation value resets the velocity of the pan

The display link has been invalidated but we still need to update the completion percentage as the user pans around the screen, based on the location of the touch. As the location moves along the y axis we’ll update the percentage based on the distance from it’s starting and final locations, like so:

So we need to do this calculate for every change in the gesture. Something like this will do:

1
2
3
4
5
6
CGFloat centerY = self.context.containerView.center.y;
CGFloat percentComplete = MAX(MIN(self.view.center.y / centerY, 1.0), 0.0);

if (self.reversed) percentComplete = 1.0f - percentComplete;

[self.context updateInteractiveTransition:percentComplete];

The Gesture Ends

Now might be a good time to make a fresh cup of coffee. Check Twitter. Play some Threes. Because even though we have come to the end of our pan gesture, we’ve still got a lot of work to do. We’ve got to:

  1. Decide whether the transition should finish or be cancelled
  2. Create a smooth continous-from-the-gesture animation
  3. Complete the transition as soon as the view is offscreen
  4. Deal with both the forward and reverse cases.

Let’s get started :)

Deciding to finish or cancel

Before the gesture ends, the transition is in limbo. The user can decide either to finish the transition, or to cancel it. So we have to glean the meaning of the gesture that hopefully corresponds to the users assumptions about how it should work. Most tutorials I’ve read on interactive transitions (even the ones I’ve made) just use the location of the gesture to make the decision. Something like this:

1
2
3
4
5
6
7
8
9
10
11
if (gesture.state == UIGestureRecognizerStateEnded) {
    CGPoint location = [gesture locationInView:gesture.view];

    BOOL shouldFinish = location.y > 160;

    if (shouldFinish) {
        // ...
    } else {
        // ...
    }
}

They pick a reasonable threshold value (160) and depending on the which side the location falls on. This is the naive approach. Instead, we should consider first the velocity of the gesture. If the user is swiping up, but lifts their touch in the middle of the screen, the view should continue along it’s velocity and animate up off screen. Subsequently, if the user’s finger is towards the top of the screen, but swiping down, when they let go the view it should continue towards the center. If we’d have just used location then the opposite would have happened in both cases.

If the velocity of the gesture is too low, indicating the user isn’t swiping much in any direction, then we can fallback on the location to make our decision. That could look something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if (gesture.state == UIGestureRecognizerStateEnded) {
    CGPoint location = [gesture locationInView:gesture.view];

    CGPoint velocity = [gesture velocityInView:gesture.view];
    CGPoint location = [gesture locationInView:gesture.view];

    CGFloat kTransitionGestureVelocityThreshold = 50.0f;
    CGFloat kTransitionGestureLocationThreshold = 284.0f;

    BOOL shouldFinish;

    if (ABS(velocity.y) > kTransitionGestureVelocityThreshold) {

        shouldFinish = velocity.y > 0;

    } else {

        shouldFinish = location.y > kTransitionGestureLocationThreshold;

    }

    if (self.reversed) shouldFinish = !shouldFinish;
}

Let’s take this piece-by-piece:

1
if (ABS(velocity.y) > kTransitionGestureVelocityThreshold) {

If the absolute value of the velocity in the y direction is more than some reasonable threshold (I picked 50), then velocity is enough to decide whether the transition should finish or cancel.

1
shouldFinish = velocity.y > 0;

If the y velocity is greater than 0 (which indicates the touches are traveling down the screen since the coordinate system starts in the top left) then the transition should finish.

1
2
3
4
5
} else {

    shouldFinish = location.y > kTransitionGestureLocationThreshold;

}

If the velocity isn’t large enough to decide the outcome, then fallback on the location. If the y location is over some threshold, then the transition should finish.

1
if (self.reversed) shouldFinish = !shouldFinish;

If the transition is running in reverse, then simply reverse the decision.

Now that we’ve made our decision, we have to let the transition system know that we’re either finishing or cancelling the transition, and we can do that like so:

1
2
3
4
5
if (shouldFinish) {
    [self.context finishInteractiveTransition];
} else {
    [self.context cancelInteractiveTransition];
}

This doesn’t do much for a modal transition, but for a navigation transition you want to make sure you remember to call these methods because the transition system will proceed to either finish the nav bar animation or reverse it.

Creating a smooth final animation

It’s time to animate the view to it’s final position. First we need to determine where to animate the view to, either back off the top of the screen or to the center:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CGPoint finishCenter;

if (shouldFinish) {
    if (self.reversed) {
        finishCenter = CGPointMake(self.context.containerView.center.x, -100);
    }else{
        finishCenter = self.context.containerView.center;
    }
}else{
    if (self.reversed) {
        finishCenter = self.context.containerView.center;
    }else{
        finishCenter = CGPointMake(self.context.containerView.center.x, -100);
    }
}

Then we can perform the final animation, like so:

1
2
3
4
5
6
7
[UIView animateWithDuration:0.3 animations:^{
    self.view.center = finishCenter;
} completion:^(BOOL finished) {
    [gesture.view removeGestureRecognizer:gesture];

    [self.context completeTransition:![self.context transitionWasCancelled]];
}];

Even though at this point we have a working interactive animated transition, something doesn’t exactly feel right with that finishing animation. Take a look:

The animation isn’t continuing from the velocity of the gesture, creating a feeling like we are pushing the view through goop. Unfortunately, animations driven by Core Animation aren’t able to take initial velocity into consideration, but we have some options:

  1. UIKit Dynamics
  2. Facebook POP
  3. Custom Animations using CADisplayLink

These options are all thoroughly covered in the Interactive Animations article on objc.io that you should really check out.

Even though Facebook’s POP was built with this exact kind of use-case in mind, I actually rather like using UIKit Dynamics for this sort of thing. In their tech talk introducing POP to the world UIKit Dynamics is dismissed because it’s “hard to use it in a one off situation” and you’d have to change all your apps animations to use dynamics instead. I don’t think that is the case. UIKit Dynamics can very easily be used in one-off situations like this, for when you need to “animate” a view following a gesture.

So that’s what we are going to do, and by borrowing the behavior code from the Interactive Animations article we can build this pretty quickly. The only thing we need to change is to increase the dynamic item behavior’s resistance and the attachment behavior’s damping a little bit, like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)setup {
    UIAttachmentBehavior *attachmentBehavior = [[UIAttachmentBehavior alloc] initWithItem:self.item attachedToAnchor:CGPointZero];
    attachmentBehavior.frequency = 3.5;
    attachmentBehavior.damping = 0.6; // increased from 0.4
    attachmentBehavior.length = 0;
    [self addChildBehavior:attachmentBehavior];
    self.attachmentBehavior = attachmentBehavior;

    UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[self.item]];
    itemBehavior.density = 100;
    itemBehavior.resistance = 25; // increased from 10
    [self addChildBehavior:itemBehavior];
    self.itemBehavior = itemBehavior;
}

We’ll setup the dynamic animator back in the startInteractiveTransition: method and use the container view as it’s reference:

1
2
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:container];
self.animator.delegate = self;

Then back over in the gesture handler, we need to create the PaneBehavior when the gesture ends (I renamed the class FinishingBehavior)

1
2
3
4
5
6
self.finishingBehavior = [[FinishingBehavior alloc] initWithItem:self.view];
self.finishingBehavior.targetPoint = finishCenter;

if (!CGPointEqualToPoint(velocity, CGPointZero)) {
    self.finishingBehavior.velocity = velocity;
}

Before we add the behavior to the animator (thus kicking off the dynamic system) we can assign an action block to the behavior which the animator will call on every “tick” of the system, and use that block to figure out if the view leaves the container view. When the view does leave the container view, we can stop the dynamic system by removing all the behaviors from the animator (this takes care of Step 3 from above):

1
2
3
4
5
6
7
__weak typeof(self) weakSelf = self;

self.finishingBehavior.action = ^{
    if (!CGRectIntersectsRect(gesture.view.frame, weakSelf.view.frame)) {
        [weakSelf.animator removeAllBehaviors];
    }
};

When all the behaviors are removed from the animator, or the view settles into place in the center of the screen, the dynamicAnimatorDidPause: delegate method will be called (remember we set the delegate on the animator to self when we created it). This is where we’ll be cleaning up our transition:

1
2
3
4
5
6
7
8
9
10
- (void)dynamicAnimatorDidPause:(UIDynamicAnimator *)animator
{
    [self.animator removeAllBehaviors];

    for (UIGestureRecognizer *gesture in [[self.context containerView] gestureRecognizers]) {
        [gesture.view removeGestureRecognizer:gesture];
    }

    [self.context completeTransition:![self.context transitionWasCancelled]];
}

That’s it! Phew. Grab all the code from this gist.

Wrap up

The things I’d really like you to takeaway from this post is to push the boundaries of animated transitions by making all transitions interactive, even if they start with an animation.

We are entering into a new world of Transitional Interfaces and so even though this may seem like a lot of work for little benefit, to me it’s worth it. And it’s not that much code, the entire example project weighs in at just over 400 lines. I’ve put it in a gist if you’d like to kick the tires.

If you have any questions or feedback (or a better way to do this) please contact me on twitter.


  1. I’m using position here because that is what we are animating. It’d work the same way if we were animating any other animatable property, such as backgroundColor or alpha.

  2. Again, if you are animating other animatable properties other than position, you’d have to copy those from the presentation layer to the model layer too.

AFNetworking SSL Pinning With Self-Signed Certificates

Rob Napier’s recent talk on iOS security inspired me to implement certificate pinning for an iOS app I am working on1. If you haven’t heard of it before, there is a good introduction to the technique over at the Open Web Application Security Project. The basic idea is that, if you control both the server and client that want to talk over https, there is no need to use third-party Certificate Authorities (of which there are many you explicitly trust without using pinning), because you already have knowledge of the certificate the server will use, so you can just take that server certificate and put it in your client. Then when the client wants to connect to the server it can just match the bundled certificate with the one from the server.

It’s a fairly new idea that gained traction back in 2011 when Chrome implemented pinning for google.com which allowed it to expose a man-in-the-middle attack caused by a fraudulent SSL certificate issued by the CA DigiNotar.

AFNetworking 2.2.1 was recently released with updates to it’s SSL pinning with self-signed certificate abilities that makes this relatively easy to implement. The tricky part is all the different ways you can generate certificates, configuring the server, the different formats, and generally putting it all together. Below is how I got it to work for my app, hopefully it will be helpful to someone else looking to do the same thing.

The self-signed certificate question

The first problem I ran into is whether or not I should use a self-signed certificate, instead of a certificate issued by a third-party authority such as Verisign2. I chose to go the route of a self-signed certificate, because I wanted to pin my app to my own self-signed Certificate Authority (CA)3. This means that my app will connect to any server that uses certificates issued by my self-signed CA. This is safer then pinning to a specific certificate because I didn’t want to worry about updating clients for expiring certificates. This is Rob Napier’s advice from his talk mentioned above.

Note: If your server is also going to be used as a public facing website (and not just an API server like the one I am building), and want to serve content over HTTPS, you are going to need to go the third-party CA route, since if you serve a self-signed cert to a browser, the user will end up just seeing a distressing message about a bum certificate.

Create the CA and certs

Apple has a great technical note for creating self-signed CA’s and certs. It’s ostensibly about doing this for testing purposes, but it should work just fine for production apps using pinning. I followed the directions in the technical note exactly and ended up with all the certs I needed for implementing pinning.

Configuring the Server

After I created the appropriate certs, I needed to setup my server for SSL. I’m using nginx and it’s fairly straightforward to setup. I uploaded the exported self-signed certificate (the “p12” file) to the /etc/nginx/ssl directory on my server using scp:

1
$ scp ServerCertificate.p12 user@myserver.com:/etc/nginx/ssl

Note: Make sure to upload the p12 file to the server before extracting the private key and certificate so your private key stays safe during transit.

Next, I extracted the server certificate’s private key and cert (entering the password I set during export):

1
2
user@myserver.com: $ openssl pkcs12 -in ServerCertificate.p12 -nokeys -out server.crt
user@myserver.com: $ openssl pkcs12 -in ServerCertificate.p12 -nocerts -nodes -out server.key

Now I had two files on my server, /etc/nginx/ssl/server.crt and /etc/nginx/ssl/server.key. I updated my nginx config (usually found at /etc/nginx/nginx.conf) to flip on ssl for my server:

1
2
3
4
5
6
7
8
 server {
    listen 443;
    server_name myserver.com;
    root /path/to/my/root;
    ssl on;
    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
}

Note: You don’t need to have the CA root certificate on the server, or concatenate the CA root cert with the server.crt file or anything like that. I spent too much time working under that assumption and took a couple of wrong turns before realizing it wasn’t necessary.

After restarting nginx I made sure it was working by downloading the server’s certificate using openssl, like so:

1
$ openssl s_client -connect myserver.com:443

This printed a bunch of information about the SSL certificate for myserver.com. The output had “Verify return code: 21 (unable to verify the first certificate)”. This just means that openssl couldn’t verify the root certificate (since it doesn’t know anything about my self-signed CA cert). When I passed in my CA certificate the SSL handshake resulted in “Verify return code: 0 (ok)”:

1
$ openssl s_client -connect myserver.com:443 -CAfile ./MyCACertificate.pem

Note: openssl expects the CA cert to be in PEM format. Convert it to PEM using openssl x509 -inform DER -in MyCACertificate.cer -out MyCACertificate.pem

Pinning the app with AFNetworking 2.2.1

AFNetworking 2.2.1 has been released that makes it easy to use self-signed certs for pinning (2.2.0 had some problems). All you need to do is to set the security policy of your AFURLSessionManager or AFHTTPRequestOperationManager subclass to use AFSSLPinningModeCertificate, like so:

1
2
3
4
// This usually would be a subclass of AFHTTPSessionManager
AFHTTPSessionManager *client = [[AFHTTPSessionManager alloc] initWithBaseURL:baseURL sessionConfiguration:configuration];
client.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
client.securityPolicy.allowInvalidCertificates = YES;

One thing to note is I needed to set allowInvalidCertificates to YES because I am using self-signed certificates (I’m not a big fan of the name of that property. It really just means “allow self-signed certificates”).

Next, I added my CA certificate to my project (so AFNetworking can access it through the main bundle) with the extension cer, since AFNetworking specifically looks for certificates with that extension. The certificate needs to be in DER format to work, not the PEM format. If you created the CA certificate using Keychain Access, then it should already be in DER format (you can tell by inspecting the contents of the file. If it’s in binary then you’ve got a DER formatted certificate). If you only have it in PEM format you can convert it to DER like so:

1
$ openssl x509 -in MyCA.pem -outform der -out MyCA.cer

At this point I finally had SSL pinning with self-signed certificates working.

Thoughts, Questions, Concerns, and Confusion

  • If I wasn’t using AFNetworking then I would just use RNPinnedCertValidator, leaving everything else the same.

  • This is making me paranoid about my server’s private keys and my own computer’s security. I know just enough to be dangerous. This whole week-long investigation makes me appreciate the hell out of people who do sysops.

  • How does everyone else do pinning? Also, what’s the difference between certificate and public key pinning? Is one better than the other? Why are there two different ways to do this?

  • The iOS Security APIs are WAY behind of OS X’s. I’m guessing most of the discrepency is because on iOS you are supposed to fail if cert trust can’t be established, where on OS X you are given a chance to recover from failure by showing the user a dialog that allows them to set explicit trust. Although the SecTrustGetTrustResult function on OS X doesn’t do at all what the doc suggests. It returns the same exact set of constants that SecTrustEvaluate returns, even though the docs suggest it should return actual reasons why SecTrustEvaluate failed. It’s not at all a replacement for the deprecated SecTrustGetResult. As of 10.9 there is no non-deprecated way to recover from kSecTrustResultRecoverableTrustFailure. Time to fail some radars.

  • Protecting against MITM needs to be much easier than this. It’s too easy to leak data between an app and a server. Setting up SSL should be easier than this.

  • It’s also possible that I’m just a dummy and it IS easy for everyone else, which is something I suspect to be true.

  • Almost every single tutorial I read on generating self-signed certs recommend that you put your server’s hostname in the Common Name field, even though the HTTPS RFC clearly states that you should use the subjectAltName extension for hostname checking (only old ssl clients use the Common Name for hostname checking).

Let me know if I got anything wrong by messaging me on twitter. I am afterall a cryptography tourist.


  1. That story coming soon.

  2. You don’t need to use a self-signed certificate to get the benefits of pinning. Pinning works just as well with pinning to a cert issued by a third party, although you might run into problems when the certificate expires.

  3. Just like you can create your own certificates, you can create your own certificate authorities, and then use that certificate authority to issue server certificates.

Indie Swizzling

Method swizzling has recently hit the mainstream. It’s a technique we use at Code School quite a bit. For example, if you look inside the downloaded projects for Core iOS 7, you’ll see plenty of swizzling going on and most of the time the technique described in mattt’s post is sufficient. But every once in awhile you’ll try to sizzle a method and for some strange reason it doesn’t work. It’s like the class is immune to your swizzling ways.

I ran into one of these immune classes when trying to swizzle the downloadTaskWithURL: method on NSURLSession using the traditional category-based technique. I wanted to make sure the Code School student called downloadTaskWithURL: with the correct parameters1.

Here what the calling-code looked like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSURLSessionConfiguration *default = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:default
                                                          delegate:self
                                                     delegateQueue:[NSOperationQueue mainQueue]];

    NSURL *URL = [NSURL URLWithString:@"http://initwithfunk.com"];

    NSURLSessionDownloadTask *task = [session downloadTaskWithURL:URL];
    [task resume];

    return YES;
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;

    NSLog(@"Task did complete with status: %d", response.statusCode);
}

And the swizzling category (this isn’t exactly how it worked but essentially it did the same thing):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#import "NSURLSession+Swizzle.h"
#import <objc/runtime.h>

@implementation NSURLSession (Swizzle)

+ (void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = self.class;

        SEL originalSelector = @selector(downloadTaskWithURL:);
        SEL swizzledSelector = @selector(xxx_downloadTaskWithURL:);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        IMP swizzledImp = method_getImplementation(swizzledMethod);
        IMP originalImp = method_getImplementation(originalMethod);

        BOOL didAddMethod =
        class_addMethod(class,
                        originalSelector,
                        swizzledImp,
                        method_getTypeEncoding(swizzledMethod));

        if (didAddMethod) {
            class_replaceMethod(class,
                                swizzledSelector,
                                originalImp,
                                method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }

    });
}

- (NSURLSessionDownloadTask *)xxx_downloadTaskWithURL:(NSURL *)url
{
    NSLog(@"%s", __PRETTY_FUNCTION__);

    return [self xxx_downloadTaskWithURL:url];
}

@end

When this ran, the xxx_ method would never get called. I stepped through the +load and everything seemed to be working just fine. But when I logged the pointers of the class I was swizzling in +load and the class of the NSURLSession instance I was using in the app delegate, I discovered that they were pointing to different locations in memory.

So I tried updating the +load method. Instead of using self.class, I would grab the class from an actual instance of NSURLSession, like so:

1
Class class = [NSURLSession sharedSession].class;

Unfortunately, that led to the class_getInstanceMethod(class, swizzledSelector) call returning nil since it was no longer looking in the class that included the xxx_downloadTaskWithURL: method. Replacing that code with class_getInstanceMethod(self.class, swizzledSelector) won’t work either, leading to a -[__NSCFURLSession xxx_downloadTaskWithURL:]: unrecognized selector error when the implementation of the swizzle method tries to call back to the original method that doesn’t exist.

The “Indie” Swizzle2

It turns out there is a more direct way to swizzle methods without first defining the replacement method on the same class as the original method. Mike Ash details this technique in a fantastic swizzling Friday Q&A:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// Create a function pointer for the original downloadTaskWithURL: to be stored in
static NSURLSessionDownloadTask * (*OriginalDownloadedTaskWithURL)(id, SEL, NSURL *);

// Define our custom implementation of downloadTaskWithURL:, calling OriginalDownloadedTaskWithURL
// to call the original method
static NSURLSessionDownloadTask * `(id self, SEL _cmd, NSURL *url){
    NSLog(@"%s", __PRETTY_FUNCTION__);

    NSURLSessionDownloadTask *task = OriginalDownloadedTaskWithURL(self, _cmd, url);

    return task;
}

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        // Replace the method on the same class that's used
        // In the calling code
        Class class = [NSURLSession sharedSession].class;

        SEL originalSelector = @selector(downloadTaskWithURL:);

        // The replacemethod method implementation
        IMP replacement = (IMP)CustomDownloadTaskWithURL;

        // This will eventually hold the original downloadTaskWithURL: method
        IMP *store = (IMP *)&OriginalDownloadedTaskWithURL;

        IMP originalImp = NULL;
        Method method = class_getInstanceMethod(class, originalSelector);
        if (method) {
            const char *type = method_getTypeEncoding(method);
            // Replace the original method with the XXXDownloadTaskWithURL IMP function
            originalImp = class_replaceMethod(class, originalSelector, replacement, type);
            if (!originalImp) {
                originalImp = method_getImplementation(method);
            }
        }

        // Put the original method IMP into the store pointer
        if (originalImp && store) { *store = originalImp; }
    });
}

Since all objective-c methods eventually just become a C function with the first two arguments of id self and SEL _cmd, we can just go ahead and define the replacement method as a static function called XXXDownloadTaskWithURL that accepts self, _cmd, and an NSURL * as the “first” argument.

We also create a place to store the original method, a pointer to an IMP pointer called OriginalDownloadedTaskWithURL which will allow our replacement method to call back out to the original. Of course the original description of this approach is a better so definitely check it out.

Why doesn’t category-based swizzling work for NSURLSession?

I don’t know what makes NSURLSession immune to the category based swizzle, but I’d love to (my Objective-C runtime skills are just dangerous enough to put this together). I think it has something to do with it being a class-cluster, or maybe it has to do with it being a toll-free bridged class to __NSCFURLSession. I’m not sure.

If you know the answer, or have a suggestion for further research, I’d love to hear about it and update this post. Message me on twitter @eallam.

UPDATE

Thanks to a tip from Daniel Haight it’s possible to shorten this code a bit by using imp_implementationWithBlock instead of creating a C function for the replacement method (saving us from having to name it):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Create a function pointer for the original downloadTaskWithURL: to be stored in
static NSURLSessionDownloadTask * (*OriginalDownloadedTaskWithURL)(id, SEL, NSURL *);

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        Class class = [NSURLSession sharedSession].class;

        SEL originalSelector = @selector(downloadTaskWithURL:);

        // Use this to create the replacement IMP inline
        IMP replacement = imp_implementationWithBlock(^NSURLSessionDownloadTask * (id _self, NSURL *url){

            NSLog(@"%s", __PRETTY_FUNCTION__);

            return OriginalDownloadedTaskWithURL(_self, _cmd, url);
        });

        IMP *store = (IMP *)&OriginalDownloadedTaskWithURL;

        IMP originalImp = NULL;
        Method method = class_getInstanceMethod(class, originalSelector);
        if (method) {
            const char *type = method_getTypeEncoding(method);
            originalImp = class_replaceMethod(class, originalSelector, replacement, type);
            if (!originalImp) {
                originalImp = method_getImplementation(method);
            }
        }
        if (originalImp && store) { *store = originalImp; }
    });
}
Icon attribution:

  1. These are downloadable throwaway projects to help our students practice and learn. So it was safe to swizzle as long as it worked consistently. I wouldn’t suggest every putting this in shipping code.

  2. “Indie” because the replacement method implementation is independent of the class being swizzled

Google Maps API Isn’t the Only Directions Game in Town

If you are building an app that includes mapping directions, you may be worried about hitting the Google Maps API limit of 2,500 direction requests within a 24 hour period before having to pay up for a higher quota.

With iOS 7, Apple has now added a directions API that is free, and only has per-device throttling. That means that if your app goes full on Flappy Bird, you won’t need to worry about having to pay up1.

Like the Maps app itself, MKDirections currently only supports walking and driving directions (although keep an eye out for the MKDirectionsTransportType enum in future API diffs).

It’s a fairly simple API to use. First, you create a MKDirectionsRequest object:

1
2
3
4
5
MKDirectionsRequest *request = [MKDirectionsRequest new];
request.transportType = MKDirectionsTransportTypeWalking;
request.source = [MKMapItem mapItemForCurrentLocation]; // start from the users current location
request.destination = [MKMapItem initWithPlacemark:selectedPlacemark];
request.departureDate = [NSDate date]; // Departing now

Then create a MKDirections instance (storing in a property if we need to cancel it later):

1
self.directions = [[MKDirections alloc] initWithRequest:request];

You can either request all the routing information between those two points2, or you can request just the estimated time of arrival3.

1
2
3
[self.directions calculateETAWithCompletionHandler:^(MKETAResponse *response, NSError *error) {
    self.walkingTime.text = [formatter stringForTimeInterval:response.expectedTravelTime];
}];

There is nothing in the documentation or App Store Guidelines to suggest that you can’t use these directions with a different mapping technology (like Google Maps or MapBox).


  1. Well, other than the 30% cut Apple takes

  2. Using the calculateDirectionsWithCompletionHandler: method

  3. Uses the TTTTimeIntervalFormatter for creating a human readable string