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.

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.

Xcode 8 iOS 10 Non-Stop Device Logs

This post is about my Stack Overflow answer.

In Xcode, Window->Devices, choosing a device and clicking the up arrow in the bottom bar brings up the console and this traditionally allowed you to view output from your app when not running in the debugger, e.g. for testing background launching behaviour. Unfortunately as of Xcode 8 and iOS 10 there is now a lot more log output – very low level debug logging is on so hundreds of lines pass by every second, making it impossible to see the logs you are interested in.

The solution is to upgrade to macOS Sierra, the new Console app now displays connected devices, and it allows filtering the log by many different categories, process is probably the most handy where you can simply enter the name of your app’s executable. This now makes the Xcode Devices log redundant, it never was very good at search anyway.

Enabling iOS 9.3 SDK in Xcode 8 for iOS 10

Some of the new iOS 10 features contain link-time checks, that is they are only enabled if the app was linked to the iOS 10 SDK. When working on an open source library it is necessary to debug when linked to an older SDK, e.g. iOS 9.3, on a newer device iOS, e.g. iOS 10. It currently isn’t possible to deploy to a newer iOS device with the old Xcode designed for the older SDK. For this reason it can be necessary to enable the older SDK in the newer Xcode, which can be done as follows:

Note: these commands assume Xcode 7 is at \Applications\Xcode.app and Xcode 8 is at \Applications\Xcode-beta.app

sudo ln -s /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk

sudo ln -s /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator9.3.sdk

sudo /usr/libexec/PlistBuddy -c "Set :MinimumSDKVersion 9.3" /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Info.plist 

sudo /usr/libexec/PlistBuddy -c "Set :MinimumSDKVersion 9.3" /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Info.plist

After running these commands, restart Xcode 8. Now you can set the Base SDK to iOS 9.3 and deploy to an iOS 10 device.

Any newer code generation features in Xcode 8 will need to be turned off, e.g. a Core Data model will need to have code generation set to none.