NSDocument UI states

... and other stories.

I'm currently converting a single window macOS app to a NSDocument based app. This is less trivial than I thought in the beginning, because there are more global hooks than expected. Here is a list to name a few:

  • Most important the database connection or NSManagedObjectContext if you use CoreData
  • Per document settings
  • NSNotificationCenter.defaultCenter
  • Some stuff hidden in NSApplicationDelegate
  • Bindings e.g. in NSArrayController instances
  • Document specific data in object which are global by nature like e.g. NSValueTransformer, NSScriptCommand, ...

Passing NSDocument

Once all relevant data like the database connection is set up it is time to propagate the reference to the document to all sub controllers in this order

  1. NSWindowController
  2. NSViewController
  3. NSView if required

Additional propagation can be done when instantiating new view controllers. This has to happen before viewDidLoad or windowDidLoad have been called in order to have bindings doing the right thing.

- (void)viewDidLoad {
    [super viewDidLoad];
    self.myViewController.document = self.document;
    // ...
 }

That means that usually this is the flow in the controller:

  1. init: No document reference is yet known,
  2. awakeFromNib / viewDidLoad / windowDidLoad: Document reference is set and can be passed to other views and controllers since view loading usually happens lazily

There is a lot going on in the Apple developer community to adopt ideas coming from web development, like from React or Elm. And there certainly is a point where current application patterns have no elegant answers, especially for complex apps and that is: state.

Here I'm trying to find my personal way for find a better solution for this, knowing both sides very well and being happy with some ideas coming from React. But I also don't want to go to far away from what Apple proposals as the standard solution, usually known as MVC.

What is meant by state? I refer to it as another way of looking at the so called model. If we hear model we usually think of a database and therefore of CoreData in the Apple universe. This is the information that influences the view. But more parameters have effects on the view, like: search, filters, view modes, selections, input, etc.

Where does this state usually go to? Well, it often end up being a property in one of many view controllers. But actually it should be part of the state.

How about putting the state on the most top level object that makes sense? For simple app this could be a global value living e.g. in NSApplication. For document based apps this should be NSDocument.

Ok, that's a good starting point. But for the later we need to make any sub view and sub view controller being aware of the NSDocument or the state. You might think this is as easy as going up to NSWindow and the look around for it, but you will pretty sure end up in situations, where you don't have access to the windows, because it isn't already there or already gone.

A better way would be to actually pass a reference around and a good spot for this is representedObject. So by overriding addWindowController: and removeWindowController: from NSDocument we have a nice entry point. From there we can go the window and pass it down via contentView.

But wait, views don't have a represented object out of th

Notes

  • Especially useful in NSDocument environments
  • Reduces need of NSNotificationCenter