Cocoa, Core Data, and me (V)

Let’s take a closer look at the radiobuttons and how they relate to core data. Remember how the countries drawer pane looks in Interface Builder:

Detail view in IB

I want the four radiobuttons to correspond to the four possible “VAT regions” in the data model:

Data model

Maybe I need to explain what I mean by “VAT regions”, as most non-europeans will find the concept literally foreign.

If the supplier and customer are both in the same country, the seller adds VAT to the invoice. If the customer is registered for VAT, he can deduct it and get it back in its entirety from the state. That’s the “domestic” case.

If the supplier and customer are in different countries within the European Community, the seller should not add VAT to the invoice, if the buyer is a registered for VAT. If the buyer is not registered for VAT, the seller should add VAT to the invoice. It’s the seller’s responsibility to check if the VAT registration number the buyer gives him is valid, and he can do that through a special web page maintained by the European Taxation and Customs Union.

Next, third kind, is (you guessed it) the USA. The USA isn’t part of the EU (yet), but there’s a treaty between the EU and the USA that says that American companies should add sales tax to sales to Europe, unless the buyer is registered for VAT.

This last part is a frustating source of misunderstanding when buying from many companies in the USA. Over there, they seem to think you have to be a corporation to be allowed to buy without sales tax. No, no, no… you have to be in the EU VAT registry, which you can be even without being incorporated. Conversely, there are corporations that do not have these VAT privileges, and which you should not sell to without VAT.

In other words, the processing of VAT for trading partners within the EU and between partners where one is in the EU and the other in the USA is the same. But we have to declare the transactions in the VAT forms in different ways for the EU and the USA, so we need to know which it is. That’s why it’s a separate VAT kind.

The final, fourth, type is “everywhere else”. Most countries will allow sales to the EU without sales tax, and the purchasing process for a EU based corporation will be the same as for purchases from the USA. Right now, in Sweden, it is even declared in the same fields on the VAT form as the purchases from the USA. But, things change, so it’s a good idea to keep them separate anyway.

Now, for sales from the EU to “everywhere else” there’s a difference. For the USA, we’re supposed to check if the buyer is a sales tax registered entity or not, which we can’t do for the rest of the world. Honestly, I’m not sure we can do it for USA based entities either, but we ought to be able to. But for the rest, I’m not sure if there are more differences or not, so I’ll stop here. I think I’ve at least given an idea of why we need the attribute “vatRegion” at all.

Binding the radiobuttons the simple way

So, how do we actually bind the radiobuttons to the database? The easiest way is to make the attribute an integer and save the index of the selected radiobutton in the database. This solution stinks, but I’m going to do it anyway. But before I explain why this solution is bad, I’ll show you how to implement it. It’s the simplest solution and it can be useful in other situations.

In XCode, open the datamodel, then click on the “vatRegions” attribute in the “country” entity. Set the type to “Integer16” like so:

vatRegion attribute setting

Now, open Interface Builder, select the radiobuttons and open the inspector. You’ll see that it is actually an NSMatrix (see the titlebar). Select “Bindings” in the dropdown, then expand the node “selectedIndex”, because that is what we’re going to be saving to the database. In the “selectedIndex”, set “Bind to” to “CountriesAC (NSArrayController)”, set “Controller Key” to “selection” and “Model Key Path” to “vatRegion”. The result should look like this:

NSMatrix Inspector

This works, so why don’t I like it? Well, we’re storing a number (0-3) in the database that records which radiobutton we selected. If we look in the database, we’ll indeed see a number, but that doesn’t help us much in knowing what it means. Bad for debugging.

If we add a fifth radiobutton later on, we have to add it last, so it gets number “4”, else we’ll screw up old data in the database. If we remove a radiobutton later, all numbers above that position in the database have to be decremented to be right. Bad for versioning.

If we have other applications using the same database for other purposes, any changes will have to be synchronized or translated. Bad for reuse.

In short: it’s bad for everything. Let’s find a better way.

Binding the radiobuttons the right way

Before we change anything, let’s see how the database looks. Open Finder and go into ~/Library/Application Support/ and you’ll find a directory “Acc2” (or corresponding, depending on how you named this application we’re building). In that folder you’ll find the file “Acc2.xml”. That’s your database. Actually, that’s kinda surprising since we haven’t ever said anything about building a database, or what it’s going to be called, or where on disk it is supposed to be saved. Or exactly when data is to be saved. Still, it’s there. As if by magic.

If you open it in a text editor, you’ll see how core data structures your information. One snippet, showing a country, could look like this:

XML database with numbers

Notice how the “vatregion” element is of type “int16” and holds the value “2”. That’s exactly the kind of ugliness we don’t want.

After changing the code to do it “right”, the database will look like this, instead:

XML data after changing VAT regions to string

Notice how the “vatregion” element is now of type “string” and how the strings are pretty darn readable. Much, much better.

By the way, since you have the old, bad, database open anyway, simply delete all the lines with the entity “vatregion” in them and numerical values, since keeping them will cause errors in the application once we change the element’s type to “string”. If it’s not there, core data will have no problem creating it with the right type. Then save the file.

Time to roll.

First, open the data model in XCode, select the “vatRegion” attribute in the “country” data entity:

Select the country entity

… then change the type of the attribute to “String”:

Attribute pane, change to string.

Now we need to add methods to the “country” entity object. Normally, we’d need to create a new managed object now, so we could add code, but as luck would have it, we already did that right at the start of episode two of this little drama series, because we needed to add in validation code for uniqueness. That means that we just open up the “country.h” and “country.m” files and start hacking away.

In the “country.h” file, add two new accessor methods, “RbVat” and “setRbVat”, after which the file looks like this:

Listing country.h

We’re going to redirect the user interface so it talks to these two new accessors instead of the “vatRegion”. That allows the UI to get and set an integer, so we can use the selected index of the radio button as before. But inside the “country” object, that integer will be translated to and from a string, which will end up in the attribute “vatRegion”, and thus on disk.

The code we have to add to the country.m file will be fairly simple:

Listing of country.m

(That set of string comparisons look really weird. Is that right, or am I too much of a n00b to be able to even do a string comparison right?)

There are three magic lines in each of these methods that are needed for the correct working of bindings and stuff, and you may admire me for knowing how to do those, but actually I don’t. I just sneaked a peek at the accessors XCode had already written for the other attributes and did the same thing. Seems to work.

Finally, we need to redirect the UI to talk to our new attribute “RbVat” instead of the old “vatRegion”. Open the Interface Builder, select the radiobuttons in the drawer pane, open the inspector, if it isn’t open already, select “Bindings” in the dropdown, then change the “Model Key Path” from “vatRegion” to “RbVat”. Things should now look like this:

NSMatrix inspector with new model key path.

Save all, build, run, enjoy. It works for me, at least.