This is, or may become, a series of blog entries about Cocoa development, as written by a complete n00b. I’m not saying it’s correct, I’m not saying I will even go on with the entries; I may stop at any time or lose interest or something. Or die, or end up in jail. Or lose my memory, or lose my website and backups. But if I don’t do any of those things, I intend to chronicle my advances into the world of OSX development. Thanks. You’re welcome. BTW, if I discover that I said something unusually stupid, I may change the text afterwards and pretend nothing happened. So if you want to prove me an idiot, you’d better take a screenshot of it before I remove the evidence. Another way of saying the same thing is: I will update the text as mistakes are discovered. Or not.
I’ll put my entries about this project into its own blog category: OSX Dev, so if you want to follow just this story, select the category in the sidepanel.
I’ve done Windows development for 20+ years and I’m sick and tired of it. I’ve been PDG (Pretty Damn Good) in a couple of languages, like compiled Basic for CP/M, Clarion for DOS, C++, and Delphi. I’ve been decently good in C#, and I’ve done my share of hating Centura. I’ve tried Java, and it left me cold.
Some languages inspire, like Clarion for DOS and to some degree Delphi. Some languages are too tedious and complex to inspire and only turn into a dreary day job, like Clarion for Windows and C#. Some languages inspire revulsion, like Centura. But that’s just me, YMMV.
Some languages are highly productive, and again, I drag up Clarion for DOS. I really love C++, but I can’t call it productive. It’s like going to the pub. You can delve into irrelevancies forever, to get everything just right, but you’re not advancing very much. There’s nothing like a perfectly tuned template to turn me on, but what’s the point? Yes, I know, there is a point, but it’s a bit too small to get actual real live and breathing software delivered.
I have a feeling that Objective-C and Cocoa may be an inspiring combination, so I started a project just to learn about it. The project is an accounting program, so it’s a database app. I’m not aiming for multiuser or anything fancy, I just want an accounting app I can change myself when I need something. I’ve done accounting apps before, so I know how they work.
To start with, I scanned the relevant newsgroups to find recommendations for books to read, and the most recommended book was Aaron Hillegass’ “Cocoa Programming for Mac OS X”, 2nd edition. It’s a good read and teaches you the basics of Cocoa with example projects. It doesn’t mention Core Data at all, since it wasn’t invented when this book was written, and that’s a big minus. High time for a new edition, I’d say.
To find out about Core Data, I used Apple’s developer resources, starting at the “Developing with Core Data” page. Two resources in particular turned out to be useful:
There’s a video tutorial on how to build a simple core data application. I watched it several times to pick up all the little details. On the same page you’ll find references to other tutorials in the sidebar.
There’s also a “Core Data Programming Guide” that I printed out and spiralbound to read in bath and bed. (You can get most of these documents on the Apple developer’s site in PDF format, see the top left of the web page.)
Keep in mind that this is my first encounter with OSX programming, so I’ll often go wrong in my implementation. Take it all with a large grain of salt.
The data model
First, I fired up XCode (v 2.4.1) and created a “Core Data Application” in the “New… project” dialog. I then opened the data model and made the following design. You have to start somewhere, and I started with “Countries”, “Customers”, and “Suppliers”. It gave me a chance to experiment with inheritance in the model, by defining both “Customers” and “Suppliers” as being derived from “Companies”.
As you can see, both customers and suppliers have a “Code” attribute. I could have put that code in the parent entity, but that would have made it impossible to have a customer and a supplier with the same code, and I can’t see why that shouldn’t be possible. OTOH, it would have made it implicit if the company in question is a supplier or a customer, making data entry just a little bit quicker at times. I’m still thinking about which way to go, but for now this is how it looks.
Obviously, the customer code, or supplier code, needs to be unique, but there’s nothing in the modelling tool that allows one to specify that, which is surprising. The only setting you have in the attribute properties panel are “Optional” and “Transient”.
If you contemplate this for a while, it becomes clear that unique keys aren’t all that important in Core Data. We’re trained to use them all over the place when working with MS SQL, for instance. But in Core Data, the framework takes care of linking entities through relationships, so most reasons for the use of unique values are gone. But not all. In my case, with the customers and suppliers, one could argue that I don’t need a code at all, and maybe I’d even agree. But when I get to general accounts, there’s no way around it. Each account must have a unique account number. Accounting rules demand it. You have to implement uniqueness constraints as validation code, but I’ll get to that later.
I do think, however, that the uniqueness constraint is important enough to have been implemented right in the data model. I hope they’ll do that in a later version.
The User Interface
There’s a real quick way of implementing user interfaces for modeled objects, and that is by drag-and-drop in Interface Builder. Simply option-drag an entity from the XCode data model and drop it on a window in Interface Builder, and you will get a fully populated UI. It isn’t beautiful, but it’s functional. The greatest advantage, however, is that it lets you figure out how to configure UI elements to talk to managed objects in the data model, without actually reading manuals.
A user interface produced in this way looks like this (I did add the search box myself, though):
As you can see, it’s an unholy mix of browsing and formbased editing. Not only that, but all fields are editable, both in the form part and the table part. The interesting parts of this, however, is what you see in Interface Builder when you examine the form elements.
The rightmost column in the browser table is actually a popup list that allows you to select one of the available countries right in the browser:
To find out how this is done, open Interface Builder and select the window. Doubleclick in the table, then click once in the rightmost column (Country). Now things should look like this:
Note the little triangle in the column header. That triangle indicates that a formatter has been added to the column. Open the inspector panel, if it isn’t already open, by hitting cmd-shft-I. Click somewhere in the “Country” column and the titlebar in the inspector should say “NSTableColumn Inspector”. Now click on the triangle in the column header and the inspector should say “NSPopupButtonCell Inspector”, which tells us that the formatter is an “NSPopupButtonCell”. That’s good to know, since we now know how to do this nifty trick.
While we’re at it, we can click the “Country” element near the bottom of the window to see what that is made of, and we’ll see that it is an “NSPopupButton” as shown in the titlebar of the inspector pane.
Now that we’ve found out these little things about how a UI can be produced, we’ll put it aside and start over trying to decide how our UI should look.
Our UI guiding principles
Before actually designing our own UI, we need to philosophise a bit about it first. Accounting software as I know it, uses a lot of popup windows to dig into underlying data tables. OSX software, on the other hand, generally is very “flat” and devoid of popups. I have a hard time finding applications I can use as an example for my own app in this regard.
What I need is a UI concept that lets the user go on working in a straight line. To illustrate, I can use the entry of an invoice:
- Open an entry screen for invoices
- In the suppliers code field, pop up a list of suppliers, note that the current supplier is not in the list
- Create a new supplier, note that the country is not in the list of countries
- Create the country and select it
- Finalize creating the supplier and select the supplier
- Back in the invoice data entry, enter the totals and date and stuff
- Select each general account you need, possibly creating it on the fly, using the general accounts browser
As you can see, there’s a stack of browsers and forms that pop up and are closed in order, without ever forcing the user to back out of the data entry sequence. I hate those apps that make you stop your data entry to go somewhere else to add an account (for instance) then start over hoping that nothing else is missing. This way of working is highly productive and intuitive for the user, but risks making a mess of the UI instead. It’s also highly un-Mac. So which layout could I use to make it look good and not be confusing?
I figured that having each browser combined with a form for details would do it. I don’t want the form to show up all the time and I don’t want it to pop up just anywhere on the screen; there are enough windows already. So I decided to try using drawers. It could look like this, for instance:
To the left, the disclosure triangle (bottom right) is closed, so there’s no drawer. To the right, after clicking the triangle, the drawer appears. The idea is that the browser would allow you to select a country and return to the caller, or you could click the “+” button, which would add a row and automatically slide out the drawer to add the details.
This countries browser could be called up from the main menu or from any place in the entire accounting app where one needs to select a country.
If this UI idea holds water, I would use it everywhere there is a data table used in data entry, like for countries, currencies, general accounts, customers, suppliers, users, etc.
Constructing a form with a drawer is actually very easy. After adding a nib in Interface Builder (File… New… Cocoa Window), you add in a drawer and a custom view. I called the drawer “DetailsDrawer” and the custom view “DetailsView”:
You then wire up the thing by control-dragging from the drawer to the window (“Panel” in the screenshot) and making it the “parentWindow”. You also control-drag from the drawer to the custom view (“DetailsView”) and make that one the “contentView”. You add a button to the main window (“Panel”), control-drag from there to the drawer (“DetailsDrawer”) and connect it to Target/Action “toggle”.
The “DetailsView” pane is where you actually add the controls that will appear on screen when the drawer slides out:
The “DetailsDrawer” is just a controller, a non-visible object that ties it all together.