About
This is the M Cubed Software weblog. To find out more about us head to our about page.
Search
Feed
Archives
- June 2010
- April 2010
- March 2010
- February 2010
- January 2010
- November 2009
- August 2009
- July 2009
- May 2009
- April 2009
- March 2009
- February 2009
- January 2009
- December 2008
- November 2008
- October 2008
- September 2008
- August 2008
- July 2008
- June 2008
- May 2008
- April 2008
- March 2008
- January 2008
- December 2007
- November 2007
- October 2007
- September 2007
- August 2007
- July 2007
- June 2007
- May 2007
Building a Modern Cocoa App
Posted on 04/03/2010 at 06:13 PM in Coding
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.
The Application
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's Implementation
Justin has structured the application as 3 window controllers, 2 view controllers and the app delegate:
- MDVCAppDelegate contains all the code to initialise the Core Data store and the main window controller. It also inserts the initial authors and categories if the database is empty.
- MDVCMainWindowController is the window controller for the main window. When the window is loaded it initialises the view controllers and adds their views to the splitview, and sets up the frame and autoresizing mask.
Perhaps the most interesting part of the whole implementation is that it uses a single NSArrayController in the main window's NIB, which is then passed to the view controllers when they are initialised. This array controller is in entity mode and bound to the managedObjectContext on the application's delegate. The add button's action is connected to the array controller. - MDVCPostsListViewController is a very light weight class. It just handles initialisation and pushes all the handling into the NIB. The table column is bound to the array controller that was passed in at initialisation
- MDVCPostDetailViewController is almost identical to the posts list view controller, except that it includes methods to show the authors and categories lists. It uses the selection on the passed in array controller to fill in the details. The NIB also contains array controllers for the authors and categories lists, which are bound to the app delegates managed object context.
- MDVCCategoriesWindowController is just a singleton that sets up the main window. Again, most of the logic is pushed to the NIB with an NSArrayController handling everything.
- MDVCAuthorsWindowController is identical to the categories window controllers, except that it handles authors
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.
My Implementation
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.
- M3MasterDetailVC_AppDelegate handles all the window controllers. In this case, all the window controllers are application global. As such I prefer to put the code handling them into the app delegate. You may be wondering how the editAuthors: and editCategories: methods are called, and all will be explained later in the post.
- M3MainWindowController holds and mangages the view controllers. It initialises the view controllers in its init method and adds them to the split view in awakeFromNib. The two subviews of the splitview are connected to IBOutlets. The code for creating a new post is also in here, as I'm no longer relying on bindings for adding and removing objects.
There is also the first bit of KVO usage. The window controller observes the selectedPost property on the posts list controller. When this changes it sets the post of the details controller to the newly selected post. - M3PostsListViewController provides 5 important methods. The first returns an array of posts from the model controller. This is used by the array controller in the NIB as its contentArray. The array controller is this time used in class mode, rather than entity mode, with the contained class set to M3Post. The reload data method tells KVO that the posts array has changed, which causes the array controller to refresh.
The selected post method returns the post object that is currently selected in the array controller, if there is one. Conversely selectPost sets the selected post on the array controller. And finally there is the tableViewSelectionDidChange: delegate method, which informs KVO that the selectedPost changed whenever the table selection changes. - M3PostDetailViewController only contains code for observing and returning the list of authors and categories. Most of the logic is in the nib, where an NSObjectController represents the selected post. Other than this the NIB is almost identical to Justin's version
- M3AuthorsWindowController contains code for listing, adding and deleting authors. The authors are displayed in the UI via an NSArrayController whose content array is set to the authors array. The add and delete actions are connected to the class which calls the model controller and invokes the appropriate KVO methods.
- M3CategoriesWindowController is identical to the authors window controller, except that it deals with categories
- M3ModelController is the main difference in the two implementations. It provides a single focus point through which all data creation, deletion and retrieval flows. While this may seem like overkill for this application, where bindings are pervasive, it is a very useful thing to have when you do a lot of work with data that doesn't involve bindings.
The init methods sets up a core data manger and a simple core data instance. It also checks if the database is empty (there are no authors) and if so inserts the initial content. The rest of the methods just deal with returning, creating or deleting objects.
The following two classes I group together as M3CoreData. They are classes I've built that make CoreData cleaner and simpler to access.
- M3CoreDataManager is where all the CoreData initialisation code that often resides in app delegates is pushed off to. It doesn't do anything special, it just makes life cleaner by pushing all the code you don't care about off into a class and putting the bits you do care about into an initialisation method.
- M3SimpleCoreData is where I encapsulate the code required to add and retrieve objects with core data. This code is very samey and very tedious and there's no good reason to write it every time.
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.
Responder Chain Hacking
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).
Where To Use Bindings
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?
- Preferences - bindings are ideal for handling your preferences. Most preferences are just booleans, strings or integers, handled by checkboxes, text fields and popup lists/radio buttons. These can be bound directly to the shared user defaults controller and remove the vast majority of code from your preferences controller.
- Forms - this is sort of a superset of the preferences use case. Anywhere that you have a form for displaying and/or editing data on an object (or especially in cases of multiple selections), you should use bindings. It saves on a huge amount of code and it handles all the issues of editing multiple items at once, populating pop up lists etc
- Simple Tables - I have found that any sort of simple table should be implemented using bindings. By simple I mean ones that don't require any complicated logic in the data source. If you just have an array of objects and you want to display their properties in a table, use bindings, it is much simpler. However, do NOT use bindings for outline views. NSTreeController is evil and should be avoided like the plague. Consider this a public service warning, just avoid it and save yourself a lot of pain.
Use New Tech, It's There For A Reason
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.
(5) Comments
Comments
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…
Tom
Posted by Tomaz on 08/03/2010 at 01:30 PM
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.
Posted by M Cubed on 08/03/2010 at 01:58 PM
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
Posted by Tomaz on 08/03/2010 at 02:12 PM
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?
Posted by Scott on 10/04/2010 at 04:38 PM
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.