The Reason NSFetchedResultsChangeUpdate Is so Buggy

Always wondered why NSFetchedResultsChangeUpdate is so buggy? The reason is Apple don’t actually use it in their own apps. Instead, what they do is have a UITableViewCell subclass with a custom property setter for the object where they add KVO for only the properties they are interested in updates to, e.g. ones that require updating the cell’s views. This is a pretty good optimisation if you have a complicated object that has lots of non-user properties being updated all the time, e.g. that have server info for remote objects. They don’t go as far as updating each label for each key change, they just update all the views when any of them change. They do some times use `NSObject`’s `cancelPerformSelector` to batch the updates into one UI update. And they might also cache some data, e.g. a count of relational data. They also can use KVO contexts in really interesting ways, like using the context to “group” key paths that all have the same outcome, which saves checking the key is say in a certain array of keys that require view updates.

It makes me so sad to see so many people struggle with `NSFetchedResultsChangeUpdate` and `NSFetchedResultsChangeMove` I wish more people knew to instead go the KVO route. I think it would be cool if when creating an Xcode project they had varying levels of templates. So this KVO usage could perhaps be in an advanced core data app template and the original in a novice one, along with a health warning about not using it if you have table sections or have objects with many non-user visible properties.

I put an example in this sample code. You can see in the example controller there is no longer `NSFetchedResultsChangeUpdate` being used, and also note the workaround for UITableView moveRows not working properly across sections, also something Apple do.

New Way to Override NSManagedObject Properties

There is a new, yet not very well-known way to override NSManagedObject properties without needing to manually call KVO methods willChangeValueForKey etc. This is achieved using dynamic accessors prefixed with managedObjectOriginal as follows:

Department.h

@interface Department : NSManagedObject
@property (nullable, nonatomic, copy) NSString *name;
@end

Department.m

@interface Department (DynamicAccessors)
- (NSString *)managedObjectOriginal_name;
- (void)managedObjectOriginal_setName:(NSString *)newName;
@end

@implementation Department

@dynamic name;

- (NSString *)name
{
    // invoke the dynamic implementation of name
    NSString *name = [self managedObjectOriginal_name];
    // your custom code
    return name;
}

- (void)setName:(NSString *)name
{
    // invoke the dynamic implementation of setName
    [self managedObjectOriginal_setName:(NSString *)name;
    // your custom code
}

@end

As seen at bottom of What’s New in Core Data in macOS 10.12, iOS 10.0, tvOS 10.0, and watchOS 3.0.

Common Init for a UIViewController Subclass

When creating a UIViewController subclass, e.g. in a framework, which you require to support being created from both storyboards and code, it is useful to implement initialisation which works for both. That can be achieved with a common init routine as follows:

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        [self doCommonInit];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder{
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self doCommonInit];
    }
    return self;
}

- (void)doCommonInit{
    // init for both storyboards and code
}

Note: don’t call it _doCommonInit because Apple use that name for their own common init method! Actually it’s best avoid underscore prefix in any methods, cause that’s reserved for Apple’s use.

How to change the screenshot save folder on your Mac

By default, screenshots save to the Desktop which isn’t ideal. With this terminal command you can change the folder, e.g. to the Pictures folder:

defaults write com.apple.screencapture location ~/Pictures

To remove the setting and go back to the default do:

defaults delete com.apple.screencapture location

To query what the current setting is do:

defaults read com.apple.screencapture location

Here are all the key combos for taking screenshots:

command+shift+3 capture whole screen.

command+shift+4 capture drag to capture area.

command+shift+4 then space and click window to capture.

If you would prefer to use a simple menu bar utility for taking screenshots try my app ScreenshotMenu in the Mac App Store.

Revisiting Hackintosh

Like many others I’ve been unhappy with the lack of Mac hardware lately so thought I’d revisit the world of osx86 after many years. There seems to be many more people involved in the scene now including some excellent how-to Youtube videos even by some high profile Mac blogs and building some absolute beasts of machines. My story is I use a 2012 Mac Mini as a server and that is limited to USB 2 so was looking to upgrade, however not only is the current mini 2 years old but doesn’t have user replaceable drives or RAM so a straightforward Mini upgrade was out. Also I use a mid-2015 15″ 16GB 256GB MacBook Pro which despite all efforts to remove everything non-essential I’m running with only a few gig free, which makes working with large IPSWs painful. Furthermore because I typically have at least Xcode, Intellij, Hopper, Textmate, Transmit and Safari with at least 10 tabs open I find the 16GB memory too limiting and I in fact run most of the time with a 8GB page file which again eats into my SSD. I considered getting a new MacBook Pro with 512GB however 16GB RAM is still the max, plus I wasn’t ready to sacrifice ports or function keys. I have felt let down by Apple when I upgraded from the 6 to 6S for 3D Touch which I hardly ever use. To solve both my problems I thought I would built a high-end Hackintosh to be used both as a development machine and server, and then use a lighter travel laptop instead for casual work, perhaps a 13″ function key MBP. There are 2 things I’d have to deal with though. I tend to work a lot in coffee shops so would need to give that up and spend more time at home. Also I would need to investigate syncing large code projects between machines pre-commit, however I have an idea about that. Because a lot of my work lately has been on sync systems I think it is time to embrace a multi-device world and looking forward to the challenge.

Modern way to register NSUserDefaults

Here is a modern way to register user defaults. It uses dispatch_once to ensure the registration only happens once and performs it lazily on the first access. Keeps your defaults all on the one place. Also it is implemented as a global block in the class.

NSInteger (^countBeforeAlerting)(void) = ^NSInteger(void) {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [[NSUserDefaults standardUserDefaults] registerDefaults:@{@"CountBeforeAlerting" : @10}];
    });
    return [[NSUserDefaults standardUserDefaults] integerForKey:@"CountBeforeAlerting"];
};

Where to find IOKit headers for iOS

Sometimes when compiling more sophisticated iOS projects you need the IOKit headers which were removed from the iOS SDK a few versions ago. A simple workaround is to copy them from the Mac OS SDK to the iOS SDK with these commands:

sudo cp -r /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/IOKit.framework/Versions/A/Headers /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/IOKit.framework

Sometimes you also need OSTypes.h from libkern:

sudo cp /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/libkern/OSTypes.h /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/libkern

If you still have build errors check you are building for device and not simulator.

Default behaviour of UISplitViewController collapseSecondaryViewController

The documentation for UISplitViewControllerDelegate collapseSecondaryViewController says:

When you return NO, the split view controller calls the collapseSecondaryViewController:forSplitViewController: method of the primary view controller, giving it a chance to do something with the secondary view controller’s content. Most view controllers do nothing by default but the UINavigationControllerclass responds by pushing the secondary view controller onto its navigation stack.

Similarily, for separateSecondaryViewControllerFromPrimaryViewController it says:

When you return nil from this method, the split view controller calls the primary view controller’s separateSecondaryViewControllerForSplitViewController: method, giving it a chance to designate an appropriate secondary view controller. Most view controllers do nothing by default but the UINavigationControllerclass responds by popping and returning the view controller from the top of its navigation stack.

I thought it might be interesting to try to implement this magic behaviour to help understand what is going on and thus provide a starting point for customisation.

- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController {
    
    if ([secondaryViewController isKindOfClass:[UINavigationController class]] && [[(UINavigationController *)secondaryViewController topViewController] isKindOfClass:[DetailViewController class]] && ([(DetailViewController *)[(UINavigationController *)secondaryViewController topViewController] detailItem] == nil)) {
        // Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
        return YES;
    } else {
        
        // push the secondary view controller onto the primary's navigation stack
        UINavigationController *nav = (UINavigationController *)primaryViewController;
        [nav pushViewController:secondaryViewController animated:NO];
        return YES;

    }
}

- (nullable UIViewController *)splitViewController:(UISplitViewController *)splitViewController separateSecondaryViewControllerFromPrimaryViewController:(UIViewController *)primaryViewController{

    // respond by popping and returning the view controller from the top of its navigation stack
    UINavigationController *nav = (UINavigationController *)primaryViewController;
    return [nav popViewControllerAnimated:NO];

}

It’s strange it is possible to push the secondary navigation controller , since usually that exceptions with “Pushing a navigation controller is not supported”. Turns out within the push method it checks a private property _allowNestedNavigationControllers to allow it to pass in this case, and it must have been set by the split view controller at some point. These kind of tricks are really annoying because gives inconsistent behaviour and thus lowers developer confidence in the APIs.
It’s interesting to call: [(UINavigationController *)secondaryViewController setViewControllers:nil]; before pushing because it throws an exception that proves that nested navigation controllers are being used: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Cannot display a nested UINavigationController with zero viewControllers

I find using the plus iPhone simulator useful for testing the split controller because in portrait it is compact width but rotating to landscape it moves to regular width.

CloudPhotos – Apple CloudKit Sample

CloudPhotos : Using CloudKit with iOS is an interesting Apple sample project, last updated a year ago, I thought I would blog about some observations I had.

The table view controller uses an array of CKRecords for its data. I sort of remember Apple saying not to use CKRecord as your model class, but all I could find was this in the docs:

Customizing Records
The CKRecord class does not support any special customizations and should not be subclassed. Use this class as-is to manage data coming from or going to the server.

This doesn’t exactly say out-right don’t use as the model, but if they say don’t subclass it then to add your methods I suppose you might try to use a category. The thing is the CKRecord contains useful logic for change tracking, e.g. when modifying a property it stores the key in changedKeys. Then when you come to use a CKModifyRecordsOperation if you use the savePolicy save changed keys then it optimises by only sending those keys that were changed (I think). I suppose the issue here is this is a lot of useful behaviour you wouldn’t want to re-implement yourself in your own model class, so you do in fact want to be using CKRecord as your model class for things that will be sent back to the server. So why Apple say not to subclass it is a bit of a mystery. Anyway once you start to implement a local cache, or would like to make use of core data fetch controller and sorting, you likely will be using NSManagedObjects as your model classes. The CKRecord encodeSystemFields does not include the changedKeys array, I suppose what you can do is track the changed keys yourself on the managed object, and then when you come to transmit the record only set the keys that you want to be sent, and then CKRecord probably won’t mind it is missing the other information.

APLCloudManager is actually a controller, and is init from the app delegate. It sort of the MVC-N design (amazing video well worth the watch), where all network code is inside one controller and not in the view controllers. I say sort of because you are supposed to only manipulate the model inside here, and then model should notify the view controller new data is available, in Apples case their model is in their view controller and is updated via the completionHandler. It’s funny because in the video he says some people choose controller (who disagree controller should only be used in view controllers), and some use manager and right here is a manager example! So in this class we see all the methods for doing cloud network calls. At first I thought the saveRecord was strange because it looks like a direct wrapper around CKDatabase’s saveRecord. However it also has a dispatch back to main queue with in. This is cool because it means the UI view controller callbacks don’t need to worry at all about threading! Perhaps a good design tip to make use of. Maybe they should have at least named it savePhotoRecord though? That’s the only thing its used for. I suppose keeping it generic is ok, given the record itself is what contains the type. Actually one of the weirder things about CloudKit is in one batch you can save records of all different types in one batch, whereas CRUD API consumers would usually be saving one record type to each end point which would need multiple requests.

Again on this manager class, all the methods are void returns, meaning unfortunately there is no cancellation capability. I think Apple should have included cancellation on at least one of them to show people how it is done. Basically I think the method could return the NSOperation (wouldn’t work with convenience methods, would always need full operations) or a protocol (to hide that scary class from UI devs), then that has the cancel method. So for example if trying to refresh a table, then the user moves away, in the view will disappear the request could be cancelled.

The subscribe method in the manager is a particularly nasty one, no completion handler on it. It’s called from the main view controller’s viewDidLoad so if it fails then the user carries on none-the-wiser. Inside the method it does look for a not authenticated error, in which case it keeps retrying every 3 seconds. At this point I noticed the operation’s qualityOfService was not set to user interactive. This means it operates in the NSURLSession discretionary mode where it does fail with network errors and just keeps retrying on its own. I just wonder is this enough to mean that you don’t need any error handling at all? Hence why they didn’t even bother with a completion handler? Will need to look more into that. I think tho at least the user could have none not to leave the app until the subscription had been set up. I was thinking this kind of initialisation feature of checking for account/creating zones/creating subscriptions might be best done in an on-boarding UI screen. You know the kind that is like a welcome screen where there is some info, maybe a spinner while things are being set up then a button? The Apple News app has this on first launch.

In the APLMainTableViewController I’m dissapointed that upon a pull-to-refresh it calls loadPhotos which does a full download of all the photos again, and then it does a full table reload. Ideally we would want to download the changes and then insert/update records. Then we realise they are using the public database which doesn’t support the fetch changes feature, so now we know why they are doing a full download. They still however could have done the table delta updates, but maybe that was too much code for this intro to CloudKit.

Finally I’d like to make a general comment on this old project and why perhaps it is no longer a good beginner sample. CloudKit is all about privacy, so sharing photos to a public database does seem to conflict with that just a bit. In iOS 10 they added CKShare and secure sharing capabilities between friends, it would be great to see a photo sharing sample that uses that instead. I think most people that are struggling with CloudKit are trying to do caching or synchronisation, this sample doesn’t touch on that at all. Maybe there are other Apple CloudKit samples I haven’t discovered yet, but on my wish list would definitely be a proper sharing one, and a sync one (with silent notifications).

CloudKit Sync Nightmare

So after previous post on analysing the web requests, where we found out the classes involved and sort of when they should be used, we are still clueless as to how to structure our app to enable CloudKit syncing. We know we shouldn’t subclass fetch operation but then how should we gather our information to pass to the operation? Should we create our own operation that then start the fetch operation within? Or use queues, or dependencies? In general should we use our own queues or always use the container database’s built in queue? How should we structure our retry logic? Does NSURLSession’s discretionary mode handle CloudKit retry or just network failure retry? Should be be using longlife operations for sync? Should we be queuing or coalescing fetches between multiple pushes and between anything background and anything user like pull-to-refresh? So many unanswered questions. What a nightmare, Apple has been absolutely useless in their code samples at the WWDC talk (e.g. 16m 48s not useful example and didn’t even bother to demonstrate record sync). And what the hell do they mean by the requests sent last 24 hours / 7 days stuff? I think I need to class-dump the headers to figure out how Apple are doing this. So turns out the app uses an embedded framework for the CloudKit code, here is the dump of NewsCore on my Github, let’s take a look.

First of all we notice 336 files. 336 files just for the networking and caching, not the UI. Unbelievable, but lets carry on anyway.

A lot of the CloudKit related files are prefixed with FCCK, 27 of them! And there are many more like controllers etc. that use these objects. Maybe half the files have a reference to some aspect of CloudKit one way or another.

FCAppConfiguration looks like a possible entry point for this framework, it contains a FCCloudContext which looks like the heart of the CloudKit stuff, with many controllers, centres, managers and queues. Looks like it stores the controllers for syncing the reading list, FCReadingList which is a subclass of FCPersonalizationData : FCPrivateZoneController.

From FCModifyRecordsCommand we notice the coalesceWithCommand method so they are coalescing commands, this might eventually prevent duplicate network requests for the same thing, but doing it at the command level rather than the network level.

The operation subclass hierarchy is as follows FCCKFetchRecordChangesOperation : FCCKOperation : FCOperation : NSOperation

FCOperation isAsynchronous (no other reason for implementing that method) and childOperations suggests they are implementing operation grouping not with sub-queues but instead with dependencies between operations and tracking the group using an array, which would allow the group to be cancelled after being added to more global queue.

FCCKFetchRecordChangesOperation is like a wrapper around CKFetchRecordChangesOperation. Since that class isn’t an ivar anywhere it suggests its declared locally, which means they didn’t need to deal with self retain cycle block bugs. It has properties for everything needed to perform a fetch changes operation including: database, zone, desiredKeys, allChangedRecordsByID and allDeletedRecordIDs. It even has _continueFetchingRecords and resetForRetry methods which makes it look like the operation is being retried from within this operation. Is it added to the database queue and then addChildOperations from FCOperation though?

FCCancelHandler is an interesting class that likely is for wrapping the operation object into something that is safe for a UI developer to work with. It doesn’t appear to be used for CloudKit operations though.

FCAsyncSerialQueue shows they are using serial queues however it isn’t obvious if they are implemented via addDependency on last or with maxConcurrentOperations = 1. It is used in the FCCKRecordZoneManager.

FCPushNotificationCenter is the place where push notifications are managed.

They have added some convenience methods via FCCKDatabase+Additions.h which have completion handlers but no cancellation object returned.

Like we saw in the flat binary filenames used for caching modifications to be uploaded they are using the word command in the class names, e.g. FCModifyReadingListCommand and FCCommandQueue looks to be for managing the binary file with the loadFromDisk and serializeCommands methods.

FCCKTestDatabase makes me think they are taking advantage of dependency injection for testing purposes, where they can pass in a mocked cloud database for testing instead of doing real network requests.

To be honest I wasn’t expecting this complicated an app, this looks like at least a year of coding, maybe even more. Remember the app only syncs a few record types, reading list (bookmarks), history (read articles) and personalisation data like favourite feeds. Implementing syncing is going to be no easy task at all; no wonder we are all clueless after a few hours of WWDC talks! Hopefully some of my research has helped you though.

Sorry I didn’t link every class name, WordPress’s post editor stopped letting me set links for some reason.