Easier debugging of Core Data errors
Prompted by a friend's tweet (sorry it's late Kevin!) I was reminded of the code we've implemented to make life more pleasant when Core Data throws an error your way.
Problem 1: The alert sheet doesn't reveal enough detail
It's understandable; your customers don't care which bit of the model failed validation, they want a more helpful answer supplied by the application – or even better the error not to occur in the first place! But what about you, how do you dig into the error for more detail?
One option is to place a breakpoint on -[NSApp willPresentError:]. From there, introspect the error object in the debugger.
But this is somewhat of a pain. Getting hold of the error object in the debugger is tricky for a start! Somewhat more palatable is to log such errors to the console (probably for debug builds only, but up to you).
A nice convenient location is your app delegate:
- (NSError *)application:(NSApplication *)theApplication
willPresentError:(NSError *)error
{
// Log the error to the console for debugging
NSLog(@"Application will present error:\n%@", [error description]);
return error;
}
Problem 2: There's no easy way to see the entirety of an error's contents
The above snippet uses the built-in -[NSError description] method. It's pretty informative, but Core Data has a tendency to construct several, nested error objects. Particularly if there are multiple validation errors. Try this little beauty out in a category on NSError:
- (NSString *)debugDescription;
{
// Log the entirety of domain, code, userInfo for debugging.
// Operates recursively on underlying errors
NSMutableDictionary *dictionaryRep = [[self userInfo] mutableCopy];
[dictionaryRep setObject:[self domain]
forKey:@"domain"];
[dictionaryRep setObject:[NSNumber numberWithInteger:[self code]]
forKey:@"code"];
NSError *underlyingError = [[self userInfo] objectForKey:NSUnderlyingErrorKey];
NSString *underlyingErrorDescription = [underlyingError debugDescription];
if (underlyingErrorDescription)
{
[dictionaryRep setObject:underlyingErrorDescription
forKey:NSUnderlyingErrorKey];
}
// Finish up
NSString *result = [dictionaryRep description];
[dictionaryRep release];
return result;
}
Did you know that when you do po foo in the debugger, the string printed is actually generated by calling -[foo debugDescription]? So this neatly means that po error will give you better results.
And of course, you'll want to change the error presentation snippet at the top of the page to log using -debugDescription.
An alternative way to disable undo registration when using Core Data
As previously discussed, Core Data takes a little coaxing if you want to disable undo registration. The standard approach is:
[[self managedObjectContext] processPendingChanges]; [[[self managedObjectContext] undoManager] disableUndoRegistration]; // Make your special changes to the managed object [[self managedObjectContext] processPendingChanges]; [[[self managedObjectContext] undoManager] enableUndoRegistration];
The main point is to force through any changes the context has pending while the undo manager is still receptive, and then do the same before enabling registration again.
But what if you have other objects that have similar pending changes? Perhaps multiple MOCs attached to a single undo manager, or a custom class of your own. Or even just don't have a reference to the context itself handy. Well here's a simple alternative:
[[NSNotificationCenter defaultCenter] postNotificationName:NSUndoManagerCheckpointNotification object:undoManager]; [undoManager disableUndoRegistration]; // Make your special changes to the managed object [[NSNotificationCenter defaultCenter] postNotificationName:NSUndoManagerCheckpointNotification object:undoManager]; [undoManager enableUndoRegistration];
All managed object contexts observe their undo manager's checkpoint notification since that is the correct time for them to perform standard processing of pending changes. By posting the notification yourself, it's just an easy way to flush any changes from all relevant objects.
Customising NSArrayController arranged object removal
NSArrayController is quite possibly one of the most useful classes in Cocoa; it applies to so much situations in a modern application. Quite often you may need to subclass it to customise some aspect of the behaviour.
In particular, overriding -insertObject:atArrangedObjectIndex: is particularly handy. I use it regularly with Core Data so that inserting a managed object updates some kind of index attribute for sorting objects by. The documentation is on your side here, calling out the reason to override this method.
But what if you need to customise the behaviour when removing objects too? In the example above you could use it to update the index attribute of any remaining objects. Or your controller might be managing some unusual kind of relationship where removing an object requires extra work such as deleting unused objects.
If so, the docs are no longer on your side. No indication is given how you might do this – although there is a helpful note about the behaviour of -removeObject: with Core Data. Unsatisfied (yes, I filed rdar://7447617), I was forced to seek the answer by trial and error. So without further ado, here's the solution. Some simple drop-in code for your own custom classes:
@implementation MYArrayController
- (void)willRemoveObject:(id)object;
{
// Put your removal handling code here
}
- (void)removeObjects:(NSArray *)objects
{
// -removeObject: calls this method internally.
// Iterate the objects, reporting each one
for (id anObject in objects)
{
[self willRemoveObject:anObject];
}
[super removeObjects:objects];
}
- (void)removeObjectAtArrangedObjectIndex:(NSUInteger)index
{
// -removeObjectsAtArrangedObjectIndexes: calls this repeatedly
id object = [[self arrangedObjects] objectAtIndex:index];
[self willRemoveObject:object];
[super removeObjectAtArrangedObjectIndex:index];
}
@end
Core Data doesn't maintain relationship inverses mid-undo
I was experimenting recently with implementing a doubly linked list in Core Data as a means to store ordered relationships and discovered a rather gnarly problem. It's summarised in the title of this post, but what does that actually mean?
Let's imagine you've got an entity with a one-to-one relationship to itself.

So you can easily construct a chain of Foos, and move from one to another using -nextFoo and -previousFoo. When calling -setNextFoo: or -setPreviousFoo: Core Data very kindly maintains the other side of the relationship for you.
The problem comes while undoing or redoing a change to the relationship and you are observing the relationship, perhaps for display in the UI. Let's say you're redoing an operation that connects two Foos together. The order of operations will be something like this:
[foo1 setPrimitiveNextFoo:foo2]; [foo1 didChangeValueForKey:@"nextFoo"]; [foo2 willChangeValueForKey:@"previousFoo"]; [foo2 setPrimitivePreviousFoo:foo1]; [foo2 didChangeValueForKey:@"previousFoo"];
The issue here is that anything observing the nextFoo property of foo1 will be notified of the change before foo2's previousFoo has been updated to match. The only way to know when the relationship is back in a consistent state is to then observe foo2's previousFoo too.
If only the Core Data team had made life more pleasant by doing something more like this:
[foo1 willChangeValueForKey:@"nextFoo"]; [foo2 willChangeValueForKey:@"previousFoo"]; [foo1 setPrimitiveNextFoo:foo2]; [foo2 setPrimitivePreviousFoo:foo1]; [foo1 didChangeValueForKey:@"nextFoo"]; [foo2 didChangeValueForKey:@"previousFoo"];
Then any object observing either property will only be notified once the change is complete. Oh well.
Can I bind one object to another and have the binding use to-many property accessor methods?
Short answer: No.
Long answer:
As well as being great for UI code, Key-Value Bindings can be terrifically helpful for just tying two objects together deeper down at the controller/model layer. I use this technique quite a bit in Sandvox, but the other day wondered if I could push it further.
Let's imagine you've got two classes declared like this:
@interface ModelObject : NSObject @property(copy) NSSet *foos; - (NSMutableSet *)mutableFoos; // will send correct KVO-notifications @end @interface ControllerObject : NSObject - (NSSet *)bars; - (void)addBarsObject:(id)object; - (void)removeBarsObject:(id)object; @end
Wouldn't it be great if you could bind bars to foos? Well you can't. Key-Value Bindings don't stretch so far as to use your -addBarsObject: and -removeBarsObject: methods. Instead, it will just call [controller setValue:foos forKeyPath:@"bars"], completely ignoring the to-many accessors!
So having found this didn't work, I resigned myself to manually setting up KVO to handle the grunt work. Or did I?
Medium answer:
What?! Are you crazy? Why would you want to do this? Apple's provided a perfectly decent NSArrayController for any binding involving a to-many relationship. Use that you numpty!
Oh. Er, yeah. It seems all too often I'm trying to bypass the use of an array controller because I don't require all of its features, yet I would be far better just starting with it and ignoring that which isn't needed in the particular case.

