A while back I wrote a post on how I was pushing towards making my apps much more manageable, by separating my once monolithic app delegates and nibs into various view and window controllers. Yesterday Justin Williams wrote a post on his blog about Getting Started with Core Data, Bindings and NSViewController.
Cocoa has changed a lot over the years. I started programming on 10.3, when Xcode was a version 1.0 and Bindings, KVO and KVC were the new hotness. Over subsequent releases we gained Core Data, Objective-C 2.0, NSViewController, Garbage Collection and much more. I strongly believe that any modern Cocoa app should be using Core Data, Objective-C 2.0, Garbage Collection and NSViewController. Bindings should also be used where appropriate (I'll define where I think this is later).
Justin's post included a project he'd worked on, implementing core data tutorial application from CocoaDevCentral using several window and view controllers rather than one monolithic class and nib. The way he built his version was quite interesting, as it was differently to how I would have approached the task. As it was a relatively simple project, I thought it would be of benefit to some to provide an alternate way of building the same app. I don't think there has been two Cocoa developers giving two different ways of implementing an entire app before.
You can download the source for my version here. I also recommend downloading Justin's version. I'll first outline how Justin's implementation works and then go into how my implementation works and how it differs from Justin's.
But first it would make sense to outline the application. It is a simple blogging app. It consists of a main window containing a splitview. On the left is a list of posts, with an add button below. On the right are the details of the current post: the title, author, category and body. The author and category are chosen from a list which can be edited by clicking the edit button next to the field. This brings up a panel allowing editing of the author and category lists.
The data model is as shown below:
Justin has structured the application as 3 window controllers, 2 view controllers and the app delegate:
And that is all there is to Justin's implementation. It is very simple and most of the processing is pushed off to Core Data and NSArrayController.
I built my implementation with the same structure as my 3 existing apps. Rather than relying on core data and bindings completely, I use KVO manually and have a model controller that wraps around core data. I have a similar set up in terms of controllers: 3 window controllers, 2 view controllers and the app delegate. But I also have 9 extra classes, my model controller, 2 classes to simplify core data and 6 model classes generated by MOGenerator.
The following two classes I group together as M3CoreData. They are classes I've built that make CoreData cleaner and simpler to access.
And finally there are the 6 classes generated by MOGenerator. I've learned to swear by MOGenerator for my core data applications, it is removes all the hassle that you would otherwise have if you used Xcode's built in tool for generating model classes for core data entities. The only change from the generated code is an awakeFromInsert method in M3Post that sets the creation date.
One thing I like to do is abuse the responder chain (please don't call the police!). In larger applications I make sure every view and window controller on screen is in the responder chain. This has the advantage of being able to put methods where they belong, rather than where you can access them. For example, the newPost: method is in the window controller. In some of my apps I have it in the side bar controller, because that is all that cares about adding a post.
You can then connect you actions to the first responder proxy in the NIB. It will traverse the responder chain and find the appropriate class and then invoke the method. You can then control whether the method can be invoked by overriding respondsToSelector, which will give you menu validation control.
This is how the editAuthors: and editCategories: methods in the app delegate are called. The buttons in the details view are connected to these methods via the first responder proxy. As the app delegate is in the responder chain it will be the one receiving the methods (as nothing else futher up the chain implements them).
Bindings are a contentious issue. Some people love them, some people hate them. The fact is that they are incredibly useful and can save a LOT of code. To give one example, the new inline inspector in CCP 1.4 was initially all handled in code. Unfortunately this required a lot of logic for handling no selection, multiple selections etc. In the end I just replaced the code with bindings to an NSArrayController and managed to delete around 100-150 lines of code.
But bindings aren't useful in all cases. Over the years I've kept changing how I use bindings. I used to use them everywhere, then went through a phase where I didn't use them at all, but have now settled on a good middle ground, where I use them where they work best. So where is that?
There are a lot of people who avoid some of the new technology in Cocoa. Some don't like Core Data because it seems like voodoo (if you learn a little bit more you'll actually find that it is quite simple, yet incredibly powerful) and some dislike Garbage Collection because of experiences with other garbage collectors (in reality the GC in Obj-C not only saves you the pain of memory management but also of threading, while also potentially being faster than a retain/release system).
You should always be looking at the new technology that comes in each release of Cocoa, most of it can save you 100s, if not 1000s of lines of code if you can adopt it. It can also make the code you do have much easier to manage. It seems odd that most people would agree it is insane to write and use an alternative to something like NSTextView, yet will write and use an alternative to some more modern technology in Cocoa.
Great post - it’s similar to solution I’ve adopted, after being “beaten” by bindings a couple of times… You have it more refined though, so there are couple of things I’ll adapt, thanks
Just out of curiosity - how would you implement handling more complex model - for example, the “classic” employee/department model where array of employees depends on current department selection?
I’m thinking in direction of an “extension” class over model controller which takes selected object as parameter and handles array of children for it as well as encapsulates creation and deletion of items + KVO. This would make it reusable for each window/view controller that needs to handle it’s own selection and corresponding child objects list…
Well, much like the how details view depends on the selection of the source list. When the department changes, I would set the department variables of the employees list controller. I would then have a method that returns an array, which returns the employees for the selected department (either via the model controller or by asking the relationship, depends how your relationships are set up), which I bind my array controller to.
Thanks, it’s how I see it too… My situation is slighly more complex due to the fact that I need to use ordered arrays with custom sort indexes so I need to update these as items are added/deleted. And I need to re-use the same functionality on two forms, so extra class encapsulating it all seems like a good idea.
Thanks again, Tom
Matin, this is a really awesome post. There really aren’t that many examples of designing application architectures for Cocoa apps (particularly using Core Data) out there and I found this to be incredibly helpful. Typically, tutorials lump everything together, so it’s nice to see how a project might be sliced up and organized.
Out of curiosity, how would you modify your class structure to accommodate your source list as an NSOutlineView organized into blogs and journal entries (just an example)? Perhaps you could discuss this in it’s own article at some point?
I would add just one class, which is an object to represent an item in the outline view. This would be a basic tree data structure. I would also avoid using bindings for this and implement the outline view with a data source in M3PostsListViewController.