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.

© Mike Abdullah 2007-2015