Core Data, KVO, Bindings… Tips and Tricks for the Layman

So, as my blog has indicated, I have been trying to tackle many advanced technologies all at once, and I’m sure I will look back on this blog one day and shake my head at the silliness of the issues I’m having currently, but well, you can’t make an omelette without breaking some eggs.

I really wish I had a mentor, some guru, someone where I could just ask “am I on the right track?  Is this the right approach?”  I don’t need much supervision or direction, but just a point in the right direction would be IMMENSELY helpful.   So, without that, taking the brute force approach, I thought I’d post a few things that you, as the beginner may want to keep in mind until you are more advanced yourself.

This list will be periodically updated.

* KVO and Bindings are deep and powerful technologies (as is Core Data).  Apple has tried to hide a lot of this from you so you can get up and running quickly.  That said, it’s still a steep learning curve and it’s difficult to just ‘jump into’ KVO, Bindings, and Core Data all at once.

* Errors and exceptions relating to KVO, CoreData, and Bindings are difficult to debug as the console output of such errors is seldom descriptive enough so to help you track down the problem.

* Core Data entities should have properties that are ‘one thing’, and not a compound property.  (else you have to write a bunch of custom code)  i.e., if you for example use a Value Transformer to restore a custom object, changes to those objects’ properties will not mark the Core Data entity as dirty, and thus any changes you make to that will not get saved.  (unless you are setting these properties after just having added that object to the array, because by definition that object will be dirty.)  In slightly more programmer speak, your custom properties (transformable attributes) should be IMMUTABLE, meaning, only changes are registered if you make a call that’s something like

myEntity.myCustomProperty = someValue

NOT,

myEntity.myCustomProperty.maxValue = someMaxValue

* All the tutorials and intros to Cocoa Bindings tout it as this magic thing that will result in you never having to write glue code again!  Wrong.  F•cking wrong wrong wrong.  (At least until you get your head around it!!)  I’m actually quite surprised at how terrible some of Apple’s documentation can be on the topic of KVO, Core Data, and Bindings.  iOS Developers (which I am, 2 weeks into Cocoa…) won’t have to worry as most of this stuff doesn’t work on iOS.  For example, the easiest way to make a NSView subclass that is backed by a Core Data model.  I spent AGES trying to figure this out the KVO way, whereas the solution’s approach was briefly mentioned in ONE sentence in a KVO programmer’s guide.   (will write a post about this soon)

False Assumptions I have made along the way and what WASN’T true:

– changes to an object in a to-many relationship will notify its parent.  (i.e. I change an Employee’s name, the Department will not receive a KVO notification about this.  Only if you add/remove employees, will the Department receive an update about this.  This isn’t such an issue, but what if your object was a TimeLine with many TimeSteps, who each have a duration property?  The Timeline won’t update.  Yes yes, this is all in the KVO Documentation, and implementing such updates are non-trivial)

Advertisements

Core Data, Cocoa Bindings, OSX Programming

So, I took on a hefty task these past few days and decided to do a lot of complicated stuff all at once.  Teach myself CoreData was the original goal, but then it occurred to me there’s not much you can do with CoreData when you don’t HAVE ANY DATA.

So, knowing that I’m going to want to have a rather substantial pre-populated database in the app, and ideally delegate this menial task of populating it away to the other App designer, I thought I should create a database Editor.   This would then involve making a Mac OSX app that would make it straightforward to get data in the database.

But, if you use Mac OSX, you can leverage the extremely powerful technology called Cocoa Bindings, which is designed in a way where you shouldn’t have to write much (boring) glue code, that would be responsible for synchronizing the UI Controls / Elements with the Data they are controlling/being controlled by.

So there.  CoreData, OSX, and Cocoa Bindings, all at the same time.  Here is my qualitative report on my progress so far:

– Once you understand how things work, it is simple and VERY useful.  😀  (Yeah, ONCE you do)
– Learning a lot of things at the same time can be very difficult because one technology depends on another, and if you don’t understand any of them in great depth, it’s hard to find where the source of your problems are.
– OSX Programming is FUN.  Possibly more fun than iOS programming.

Things that tripped me up:

  • NSTextCell is used to edit text or number properties, though with numbers you will want to apply a NSNumberFormatter, and may require a subclass of NSValueTransformer to ‘translate’ the string output of a NSTextCell into a NSNumber value for your data model.
  • If you want to edit an object’s “to one” relationship with another object, don’t use a NSTextCell nor a NSComboBoxCell, but rather a NSPopUpButtonCell and follow instructions given at Apple Developer
  • If you’re using an array controller for NSManagedObjects, you should bind a NSManagedObjectContext, and use it in entity mode (not class mode), because the add: and remove: (and other such) actions of the NSArrayController will instantiate via [[SomeClass alloc] init] instead of [[SomeManagedObjectClass alloc] initWithEntity… ];
  • I still haven’t figured out how custom validation works.  It seems to validate but then reports validation failures although there shouldn’t be… Will figure that out later and in the meantime ‘will be careful’
  • Maintaining Object uniqueness can be tricky.   I added this method to a category for NSManagedObject:
+ (NSManagedObject*)findForEntityName:(NSString*)anEntityName
                            predicate:(NSPredicate*)findCriteria
                 managedObjectContext:(NSManagedObjectContext*)aContext
{
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
 	[fetchRequest setFetchLimit:1];
 	[fetchRequest setEntity: [NSEntityDescription entityForName: anEntityName inManagedObjectContext: aContext]];
 	[fetchRequest setPredicate: findCriteria];
 	NSArray *results = [aContext executeFetchRequest:fetchRequest error:nil];

 	if ([results count]==0) return nil;  // no matches (existing records with this code) were found

    return results[0];
}

Then you can quickly check if an object exists based on the entity name and the predicate (which would be filled with the query that would define uniqueness… i.e.  the property that is your “primary key” )

  • In CoreData, if you have an object that has a to-one relationship, i.e. a Recipe has many steps, and with each step there is an ingredient associated with it. However, many recipes can make use of that same ingredient.  So the step has a to-one relationship with an ingredient, and the inverse relationship has a to-many with recipe steps (and therefore indirectly to a Recipe).    Oh yeah, data modelling databases…. the one course I had, the professor was out sick for 2 months of it, and somehow nobody at my Uni seemed to care if we were receiving an education or not.  Being a German University, it’s not in their nature to care about quality, it’s in their nature to check if everyone has the stamp on their piece of paper that *proves* they are capable.
  • If you use a Transformable Attribute on your model, your subclass of NSValueTransformer has to do the following:
    – the transformedValueClass is NSData
    – Therefore the transformedValue: method should be returning NSData
    – the reverseTransformedValue: should return your custom type
    – allowsReverseTransformation should be set to YES

Things that are still a problem (will update this later when I find the solution):

  • When I create an association from a data parser (via awakeFromInsert), these aren’t being displayed in the User Interface.  However, if I create an object via the UserInterface, the associations are created.

The Most Useful Resources so far have been:

  • Ray Wenderlich Tutorials for Mac App programming (first (CoreData), second (a simple Mac OSX app), third (Cocoa Bindings))
  • Editing “to-one” relationships with Cocoa Bindings:  AppleDeveloper
  • Apple Documentation (yes, really.  Maybe I am getting better at this, or my Attention Deficit problems that arise when reading boring technical texts is not as bad as it used to be)
  • The one and only stackoverflow.com  Where would we be without that?!

But anyway, I plan to update this post a bit later.   It’s strange; now I seem to know a lot more, but am unclear how I got to this point.  There was a lot of time spent that feels like wasted time, but I guess that’s to be expected when you try to tackle a lot at once, with nobody to answer  your questions, and a lot of pre-requisite knowledge is also kind of missing.

In the end I’m thrilled that I’m learning this stuff as it only serves to make me a better developer.  At least with Apple technology, which is still really fun to work with.