Cocoa, Core Data, and me (II)

I promised to describe how to implement validation to ensure uniqueness in the database. Here goes.

First, you need to open the datamodel in XCode. With the datamodel on screen, select File… New… and you’ll see “Managed Object Class” under the section “Design”. Note that this choice simply isn’t there unless you first opened the data model.

Ok, so choose “Managed Object Class”. In the next screen you’re asked for the project. It ought to be the project you’re working on, so click “next”. Now you get to see a list of data entities from your model.

Managed Object Class Generation Assistant

What this screen does is that it subclasses NSManagedObject for the entities that you choose. You can choose to include methods for the accessors, the validation methods, or both. In my experience, the validation methods don’t get written at all if you don’t choose “Generate Accessors” as well. Probably a bug. Anyway, do what I did in the screenshot, then click “Finish”.

Open up the “country.h” file in XCode or your favourite external editor (I’m using TextMate right now, still in the trial period… looks pretty good so far):

country.h header file in the editor

As you can see, the “country” object is derived from NSManagedObject and the accessors and validators are declared. We need to include code in one of these methods, namely “validateCode”, and we’ll do that in the country.m file, of course.

Normally, the method simply contains a comment and a “return YES;” statement, but after adding in what we need, it looks like this instead:

Method implementation validateCode.

There are a number of things I like to point out regarding this code. The first is that the error handling stinks. I just plugged in the first thing that would work, so I definitely need to do some work on that later.

Another thing I recommend you take seriously is how one does fetches and predicates. The fetches are described fairly well in the “Core Data Programming Guide” I already mentioned. The predicates are described in the “Predicate Programming Guide“. The most important piece of information right now is that the “%@” placeholder in the predicate string includes the string value of the given variable, including quotes around it, while the “%K” placeholder does not include quotes.

There’s also the nifty “self != %@” construct in the predicate. The token “self” will be replaced by the objects identity in the database. If the object is new and not yet in the database, there won’t be an identity, but that’s ok. No other record with the same code should be found if the current record is entirely new, so that’s fine.

I’m sure all this code can be implemented in a library or something, so I don’t have to go through the same thing for every entity that needs a unique field value. But that’s for later. This suffices for now.

Note the NSString thing between NSFetchRequest and NSPredicate. I put that in to debug how the fetch predicate string should look, so you can take that out once you’re satisfied everything works right.

When, actually, does the validation fire?

Normally, the validation code only gets called when you save to disk, and that’s far too late. To make it fire right after you have edited the field on screen, there’s a little checkbox you need to set in Interface Builder. Open the “country.nib”, open the “DetailsView”, that is the pane that will become the drawer. Select the “Code” field, open the inspector if it isn’t already open (cmd-shift-I). Select “Bindings” in the top combobox, and then expand the “Value” row. What you’ll see is this:

Detail code field inspector.

Look closely and you’ll see a “Validates Immediately” checkbox. That’s what you need to enable to fire the validation code as soon as you’ve completed the entry in the “Code” field.

Now, when you edit the code field in the drawer and you make the code equal to a pre-existing code, an error panel rolls down:

Error duplicate code

The rest of the browser and drawer code really doesn’t work yet. You can’t get entry focus on the drawer’s fields without a lot of contortions, and you can still do data entry in the browser. This is horrible, and I intend to fix that in the next installment.