Lesson learned: CoreData rocks.

July 12, 2008

I was going to be hardcore. I was going to define all my model classes in Objective-C, and I was going to create my object models, and link them to the interface, and be all proud of my working code. And I was going to ignore that pesky problem of persistence, because that was a different battle for another time.

And then I heard about CoreData. If you don’t know what it is, it’s basically a framework that gives you a way to model the persistent data of your application (the model part of MVC) via a very UML-like graphical notation, and then provides you with a unified and consistent way to access the data.

Don’t get me wrong, BTW. Though the graphical notation is UML-like, it’s not UML. It is strictly focused only on data content (and as such does not allow you to define methods, for example). In point of actual fact, it is based on the entity-attribute-value model and as such is a database schema design modeling tool.

Still, after implementing my own quick data model shown below in about ten minutes, I was pretty pleased with CoreData. Okay, I admit it, myself too. But it’s really CoreData that should get the props.

The IntelliTrack data model.

The IntelliTrack data model.

The tricky part for me came when I tried to link the model to the application. I followed the instructions in the CoreData video tutorial slavishly, with a couple of exceptions:

  1. I had already created my application, and it wasn’t a CoreData Document-based application. It was a plain old application.
  2. I had a different plan for the UI.

I wasn’t too concerned about the second problem, since the whole point of MVC is that the UI should be pretty independent of your model. I stupidly didn’t even think about the second until, after trying to link my data to my model, I was greeted by an awkward error: my First Responder (as far as I can tell, the object that’s in charge of the application life cycle) did not respond to the managedObjectContext message. Oops.

The managed object context of an application, as explained here, is the way the application accesses all the goodies defined in the model. Since I had not created my application as a CoreData application, my First Responder was completely unaware of the model.

In order to fix this problem, I cheated. I created a new project, made it a CoreData project, and compared the generated template code with my code. I basically copied over any files that were missing from my project (including the all-important delegate that did respond to managedObjectContext), and tried to link them as in the video. There were still a few differences, due to the fact that IntelliTrack is not document based (in other words, users won’t have a “myShipments.intelliTrack” file that gets saved), but for the most part is was smooth.

As a result, I now have an application that looks like this:

IntelliTrack today!

IntelliTrack today!

And the best part? The “Add” button works, and the new items get persisted all on their own. All this in an evening’s worth of work. CoreData rocks.


False alarm

July 9, 2008

After playing a little bit, I realized that my toolbar did in fact work as “expected” (read: expected by an experienced OS X toolbar customizer).

The problem I thought I had run into was that I couldn’t drag the new elements on the toolbar. Actually, I was trying to drag them onto the default toolbar (which appears in the sheet that is activated by the “Customize Toolbar” menu item).

What I actually had to do was drag the new items onto the actual toolbar of the application. My bad. Once I realized the error of my ways, all was well. So following the steps in the previous post should be enough to create a visually complete toolbar whose customization works as expected.


Creating a Custom Cocoa Toolbar

July 7, 2008

I’ve decided to learn Cocoa and Objective-C by creating a simple application for tracking shipments. The idea is that users will be able to enter shipments and their expected arrival date, and get a little alert from the application if the arrival date has passed and the shipment has not arrived. Simple stuff, I hope. I’ll call it IntelliTrack. This will all be done in Xcode, because I cry when I don’t have Apple’s instructions on how to do things.

I began by creating a very basic application in Xcode. If you don’t know how to do that, working through this tutorial is very good to getting you started, and it’ll make following the rest of this easier. I basically don’t go over anything written there, since that tutorial is very self-contained.

With IntelliTrack set up in Xcode, I decided try to create a main window with a toolbar (easy) that has a search field (trouble).

I opened IntelliTrack/Resources/MainWindow.nib, which opened the UI of the application in Interface Builder. I selected an NSToolbar as the object to add from the library and dragged it onto the window title bar. This made the application window look like this:

A window with the default Cocoa Toolbar

A window with the default Cocoa Toolbar

Nice for a few drag-and-drop actions, but there’s no search field there. Worse, there’s stuff I don’t need, like, well everything there. I wanted a toolbar that displays only the things I want it to display, and nothing more.

To find out how NSToolbars work, I went here, and found out that toolbars have a certain default behavior that can be overridden by their delegate. Basically, a toolbar has a delegate that must implement three methods:

  1. toolbarAllowedItemIdentifiers returns the identifiers for the allowed items
  2. toolbarDefaultItemIdentifiers returns the identifiers for the items that appear in the toolbar by default if they are not overruled by the user preferences .
  3. toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar: returns the requested item.

(Note that there are other delegate methods that can optionally be implemented, as described in the NSToolbar documentation.)

Given that, I wanted a delegate that would:

  • allow all the items that the default toolbar allows, as well as a custom search field
  • by default show only the search field, flush right.

So, first things first, I created the delegate class. In Objective-C, that means I had to create a header file and an implementation file, the header file serving as the class’ interface to the world. The code for the header file can be found here, with comments to explain how things work. The corresponding implementation is here. These files reside in the IntelliTrack/Classes folder.

Fun facts:

  • Java and Ruby developers might notice that ITToolbarItemDelegate inherits from NSObject. NSObject is the equivalent in Cocoa of java.lang.Object or of Ruby’s root Object class. Inheritance must be explicitly stated because the compiler is not actually aware of the runtime. This bears repeating: the Objective-C language is separate from the Cocoa runtime provided by Apple. Objective-C simply provides a working syntax for calling objects (i.e., it will call the object you specify if it exists); it does not provide a root object to build from, or a set of libraries. In the (vaguely terrifying) words of the Cocoa documentation, if you wanted to, you could write your own root object…
  • Java and Ruby developers might also notice that most variables are prefixed with an asterisk when they are declared. This means that the variables are pointers to a value, rather than the value itself. It essentially works the same way as a regular declaration in Java or Ruby. Variables that do not get the asterisk are the actual values themselves. This is unheard of in Java and Ruby-land, but don’t ask me to go into too much detail here, as I’m just learning this myself.
  • [NSArray arrayWithObjects:firstElement...] must have nil as its final parameter. No idea why; if someone could explain it, that would be great. I do know that if that convention is not followed, running the application gives a nasty EXC_BAD_ACCESS error.

At any rate, I had to remember to turn on garbage collection, and upon building, I was ready to finish the set up of the toolbar.

Three things remained to be done: create a search view, connect the toolbar to the toolbar delegate, and connect the toolbar delegate to the search view.

First, creating the search view is easy: I just dragged the NSSearchView from the library onto the MainMenu.nib window, and it appeared. For the toolbar delegate tasks, I imported the toolbar delegate by dragging an Object item from the library onto the window, and selecting ITToolbarDelegate as the class in the identity tab of its inspector. I then connected the searchToolbarItemView outlet (defined in ITTolbarDelegate.h) to the search view. Finally, I connected the toolbar’s delegate outlet to the toolbar delegate object in the window. At the end, the window looks like this (unfortunately, the connections are not visible):

The MainMenu.nib file

The MainMenu.nib file

At last! It was ready to go. I compiled and ran (and debugged, and ran…) the application, expecting a nice toolbar with nothing but a search field. Instead, I got this:

The actual result of all my efforts. Note the depressing plethora of items.

The actual result of all my efforts. Note depressing plethora of items.

This despite the fact that the delegate method clearly returned only the flexible space and search items. The problem was that I hadn’t deleted the original items that appeared by default in the design view. As such, the toolbar thinks all those items are available (which is true) and to be displayed (which is not). It seems that the toolbar at this point either merges or concatenates the items that are displayed in the designer window with those that are returned from the delegate method, and shows the resulting set. After deleting the original items (this is done by double-clicking on the toolbar in the design view, and deleting all the items from the sheet that appears) and recompiling, I finally got the toolbar I was looking for:

Triumph at last!

Triumph at last!

Unfortunately, some bugs remained, but I’ll go over those in a future post. Please feel free to leave any comments and corrections.


By way of introduction

July 2, 2008

This is a blog about my learning Objective-C. The Apple documentation (because let’s face it, I’m learning Obj-C because I want to create pretty Aqua UIs) claims you need to know C to learn objective C. Lies, I say! I will learn it with nothing but Java and Ruby under my belt!

Please rubberneck here regularly.