Outline view, tree controller and itemForPersistentObject

Posted by Pioneer! Wed, 10 Sep 2008 12:42:56 GMT

This is the scenario: your user interface comprises an outline view and a hierarchical data model. You want the outline view to display the hierarchy and remember the expansion state automatically in preferences. Hence when the user re-runs the application, the items that were expanded are still expanded, and vice versa: what was collapsed remains so. Outline view, tree controller, hierarchical model, bindings. That’s the recipe.

According to some, storing expansion state of an outline view when used with a tree controller is not just difficult: it’s impossible! But is it? No, is the simple answer. It’s actually quite easy. In this article I introduce a new helper class called RROutlineViewExpandedItemsAutosaver! Original aren’t I? It does not involve sub-classing or access to private methods. The solution presented uses only documented interfaces and only requires a small stateless class instance for handling all outline view auto-saving technicalities. It does assume Core Data use for modelling. But you can easily adapt the technique for other data-model implementations.

It’s impossible?

At first it seems so. For a start, if you read the documentation for setAutosaveExpandedItems: you can see that you also need to implement two data-source methods: outlineView:itemForPersistentObject: and outlineView:persistentObjectForItem:. Goes without saying, you also need a data source implementing those methods. Otherwise it does not work. In fact, it’s not exactly clear what these two necessary methods are supposed to do. The document talks about translating to and from an “archived object.” What does that mean exactly?

You can find a number of posts at CocoaBuilder on this issue. Keith Blount started a thread in 2005; Nick Briggs asked about it again in 2006. But no answers forthcoming! Some discussion exists and some solutions have been posted at CocoaDev (OutlineViewCoreDataBindings and NSOutlineViewStateSavingWithNSTreeController). But there is nothing definitive. Plus, the solution supplied relies on private methods and sub-classing. Messy!

Start with a test

In the spirit of Agile, let’s start with a test that fails.

Apple’s Developer Connection provides sample code. Their AbstractTree demonstrates Core Data, use of bindings along with an NSTreeController. Download the sample here as a ZIP file. It contains two subdirectories: AbstractTree and Tutorial. The tutorial is pretty good. It should help you get up-to-speed if you need some familiarity with the basic technology. Build and run the project living under the AbstractTree folder. It presents you with a window comprising an empty outline view and two buttons.

Empty outline view with two buttons

Click Add a few times. You get a series of nested nodes named “untitled node”. Quit the application, re-run and you find that, although data saved, the expansion state of the outline view resets. You have to manually expand each item. Of course, you can Option-click the disclosure triangles to auto-expand an item and all its sub-items. But that’s not really what we want. We want the expand or collapse state of each element displayed in the outline view to persist! That’s the goal.

So far so good. We have a test case that fails. Just what we wanted.

Outline view’s auto-save name

Open the sample’s MainMenu.xib. Select the NSOutlineView. Set its Autosave name to (say) Nodes. Exact value does not matter. This is just a little piece of text for constructing the preferences key. The application’s defaults will contain an array containing the expanded items. The key will become “NSOutlineView Items Nodes” where Nodes corresponds the the specified Autosave name.

Autosave name of Nodes

Leave the Autosave Expanded Items check box unchecked! Strange but important. If you check this box, the framework resolves the expand-collapse question straightaway. It needs to wait until bindings have been fully established and the data store has been added. The application will instead enable this option programmatically when the application finishes launching. Not before.

Autosave Expanded Items check box unchecked

Add the new class

Add the interface header, RROutlineViewExpandedItemsAutosaver.h, to the project. Contents as follows. Download header and module sources here.

#import <AppKit/AppKit.h>

//------------------------------------------------------------------------------
// RROutlineViewExpandedItemsAutosaver
//------------------------------------------------------------------------------
// This class acts as a "dummy" outline-view data source. Dummy because it does
// not source any data. But you wire it up as though it did. In reality, its
// sole purpose is to enable automatic saving of an outline view's expanded
// items. It sounds counter-intuitive, but you can apply bindings through a tree
// controller and at the very same time hook up a data source (such as one of
// these) in order to provide the necessary persistence translations.
//
// Connect an outline view to an instance of this class. Note, you can connect
// multiple outlines views to the same instance! The instance carries no
// state. Message interactions with outline views pass the outline view
// instance. So no state needed.
//
// The implementation makes a number of assumptions. It assumes you connect
// using bindings to a Core Data model. Hence for every item, it sends [[item
// representedObject] objectID] which assumes that the represented object
// responds to -objectID. Core Data's managed objects respond with a unique
// object identifier.

@interface RROutlineViewExpandedItemsAutosaver : NSObject
@end

Add the source module RROutlineViewExpandedItemsAutosaver.m. Contents follow.

#import "RROutlineViewExpandedItemsAutosaver.h"

@implementation RROutlineViewExpandedItemsAutosaver

//------------------------------------------------------------------------------
#pragma mark Outline View Data Source
//------------------------------------------------------------------------------

// Although linked to the core-data context via bindings and a tree controller,
// the outline view also has an instance of this object connected to the outline
// view's data source.

- (id)outlineView:(NSOutlineView *)outlineView itemForPersistentObject:(id)object
{
    // Iterate all the items. This is not straightforward because the outline
    // view items are nested. So you cannot just iterate the rows. Rows
    // correspond to root nodes only. The outline view interface does not
    // provide any means to query the hidden children within each collapsed row
    // either. However, the root nodes do respond to -childNodes. That makes it
    // possible to walk the tree.
    NSMutableArray *items = [NSMutableArray array];
    NSInteger i, rows = [outlineView numberOfRows];
    for (i = 0; i < rows; i++)
    {
        [items addObject:[outlineView itemAtRow:i]];
    }
    for (i = 0; i < [items count] && ![object isEqualToString:[[[[[items objectAtIndex:i] representedObject] objectID] URIRepresentation] absoluteString]]; i++)
    {
        [items addObjectsFromArray:[[items objectAtIndex:i] childNodes]];
    }
    return i < [items count] ? [items objectAtIndex:i] : nil;
}

- (id)outlineView:(NSOutlineView *)outlineView persistentObjectForItem:(id)item
{
    // "Persistent object" means a unique representation of the item's object,
    // representing the objects identity, not its state. Outline view writes
    // this to user defaults as soon as the item expands. That's when it asks
    // for the persistent object, sending -outlineView:persistentObjectForItem:
    // and execution arrives here. A minor problem arises when adding new
    // items. The new item represents a new unsaved managed object. The managed
    // object only has a temporary object identifier. It will receive a
    // permanent one when saved. So, if the objectID answers a temporary one,
    // ask the context to save and re-request the objectID. The second request
    // gives a permanent identifier, assuming saving succeeds. Don't worry about
    // committing unsaved edits at this point.
    NSManagedObject *object = [item representedObject];
    NSManagedObjectID *objectID = [object objectID];
    if ([objectID isTemporaryID])
    {
        if (![[object managedObjectContext] save:NULL])
        {
            return nil;
        }
        objectID = [object objectID];
    }
    return [[objectID URIRepresentation] absoluteString];
}

@end

Add this header and module to the project, under the Classes group. An instance of this class will become the new data source for the outline view.

Add the new data-source

Add a new instance of RROutlineViewExpandedItemsAutosaver to the nib and connect the new instance to the NSOutlineView as the outline view’s dataSource. Use Interface Builder. Drag an Object from the palette to the nib. Change its class in the Identity Inspector panel (Command-6). Then connect the NSOutlineView to it as the new data source. It replaces the pre-existing connection between outline view and app delegate.

After this, the nib is ready.

App delegate

Apple have used the standard Core Data application delegate. It builds a managed object model, persistent store co-ordinator and managed object context. The classic Core Data stack! Our changes need to add a message-send for enabling auto-save expanded items to the outline view. But it’s not the only change required.

There is a subtle problem. It concerns bindings and Core Data. The important requirement is that when the outline view tries to restore auto-saved expanded items, the outline view has its items already loaded. Otherwise how can it translate “persistent objects” to items. Persistent objects, in this context, refers to keys used in the application defaults to identify the set of expanded items; everything not expanded defaults to collapsed.

Moving where the application adds its persistent store

When the application loads, it automatically loads MainMenu nib. All the object instances therein become instantiated. Indirectly, the managed object context and its other Core Data stack components come to life at this time. Therein lies the subtle problem. The Cocoa framework will not immediately load the Core Data objects if the context already has its store when nib-loading establishes the bindings. That’s a mouthful. Basically, the order must go:

  1. Establish bindings. Do this wholesale.
  2. Add persistent store. At this point, the outline view grabs its contents through bindings to the tree controller.
  3. Enable auto-save expanded items.

Simplify the -persistentStoreCoordinator implementation to:

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    if (persistentStoreCoordinator == nil) {
        persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
            initWithManagedObjectModel:[self managedObjectModel]];
    }
    return persistentStoreCoordinator;
}

And second, add a new method:

- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
    NSFileManager *fileManager;
    NSString *applicationSupportFolder = nil;
    NSURL *url;
    NSError *error;

    fileManager = [NSFileManager defaultManager];
    applicationSupportFolder = [self applicationSupportFolder];
    if (![fileManager fileExistsAtPath:applicationSupportFolder isDirectory:NULL]) {
        [fileManager createDirectoryAtPath:applicationSupportFolder attributes:nil];
    }

    url = [NSURL fileURLWithPath:[applicationSupportFolder
        stringByAppendingPathComponent:@"AbstractTree.xml"]];
    if (![[self persistentStoreCoordinator]
        addPersistentStoreWithType:NSXMLStoreType
                     configuration:nil
                               URL:url
                           options:nil
                             error:&error]) {
        [[NSApplication sharedApplication] presentError:error];
    }    
    // Watch out for this! Make sure that the data is available before enabling
    // auto-save for expanded items. You can switch it on within the nib. Problem
    // though is that the data store must be available at nib awaking time!
    // Otherwise, how can the auto-saving of expanded items compare against
    // existing items in order to determine whether or not they should be restored
    // as expanded or not.
    //
    // There's another caveat. You need to let the bindings make the necessary
    // connections first, before connecting the Core Data context and persistent
    // store coordinator to the store. In other words, the store must be added
    // last. Here is a good place. Application-did-finish-launching occurs after
    // all nib awaking methods, after bindings have been established. Hence, when
    // the store gets added, the outline view immediately sees the contents
    // through its bindings. Otherwise the outline-view expanded items auto-saver
    // cannot see the items at all.
    [outlineView setAutosaveExpandedItems:YES];
}

That’s it!

Successful test

Build and go. This time, when you expand nodes, quit then re-run, the sample correctly expands the previously-expanded items. Great. You can view the sample application’s user defaults by typing, in Terminal:

defaults read com.apple.dts.AbstractTreeApp

It will list the saved expanded items array, resembling something along these lines:

{
    "NSOutlineView Items Nodes" =     (
        "x-coredata://68A35129-6EA4-45E7-B3FB-F1C15FDC02D9/Node/p104",
        "x-coredata://68A35129-6EA4-45E7-B3FB-F1C15FDC02D9/Node/p102"
    );
}

Download the complete sample project here.


Organising view controllers

Posted by Pioneer! Fri, 29 Aug 2008 06:37:41 GMT

Understanding how individual view controllers work is one thing. Organising them is another. Applications typically deal with multiple view controllers. Views can change dynamically. Hence view controllers need dynamic capabilities.

This article presents an idea for organising view controllers within an application. Design goals include: flexibility, simplicity, convention over configuration.

View paths

The design idea centres around the concept of a view path. Paths are a ubiquitous paradigm. You find them in file systems, graphs, in Cocoa’s key-value coding, in X-Path querying, the list goes on. Graph is the underlying structure (directed acyclic, to be technical). Paths express a route between two points in the graph, from the root to some leaf node for example.

View hierarchies form such a nested model, so applying paths naturally expresses the intrinsic nested construct. Simple notion then: View hierarchy equals graph, use path to navigate it. Stay with me. You’ll see where I’m going.

Meta-control

Meta is a funny word. “It’s from the Greek,” in a Greek accent! Here it refers to things beyond or above, a higher order. View controllers control their views, yes. But what controllers the controller? Answer: some form a meta-control, controlling the control. It makes the head spin. Yet necessary. Every application, large and small, requires some form of overarching control, or, in object terms, some framework on which to overlay the flesh of behaviour.

I’m being a little cryptic. Are you following me?

So. Let’s assume: one view, one view controller. “View” here refers to a view sub-hierarchy, one that you load from a nib; so it might comprise many sub-views but has one container view. Our application wants to manage a hierarchy of such controller-view-nib triplets.

Tempting to directly overlay a tree structure by sub-classing a new view controller class, adding an array of view sub-controllers. A simple approach, but limiting. It forces the view controllers into a strict hierarchy. This does not fit the requirement for flexibility. It inserts the meta-controlling layer into the low-order control layer. A flexible solution really needs a second-order of controlling existing above the first rather than intermingled.

Show me the money

So far, we’ve been living in the abstract. Reality doesn’t live there. Let’s get practical. Take an example.

Start an experiment. Take Apple’s ViewController sample code (screen shot below) as the reference point. Let’s try to improve it. Can we reduce the number of lines? Make it easier to understand, maintain, expand? Seems like a tall order. Too tall?

ViewController sample code

As you see, there’s a large custom view in the upper portion of the window. Clicking the pop-up button presents you with four choices. The choice switches the custom view. Four different sets of view controller, nib and sub-view switch in and out accordingly.

Lies, lies and statistics

Improvement is hard to measure. Better is a subjective term. My better and your better might not be the same better. So for the experiment, we will add some objectivity. Start by measuring the existing ViewController project.

Basic software metric: lines of source code (SLOC).

The cloc tool reports:

-------------------------------------------------------------------------------
Language          files     blank   comment      code    scale   3rd gen. equiv
-------------------------------------------------------------------------------
Objective C          11       165       502       229 x   2.96 =         677.84
-------------------------------------------------------------------------------
SUM:                 11       165       502       229 x   2.96 =         677.84
-------------------------------------------------------------------------------

229 lines of code in total. I’m running cloc from the ViewController project directory and passing arguments ”--force-lang="Objective C",h .” meaning that source files with h classify as Objective C; dot refers to the current working directory, of course.

Adding the --by-file option produces some interesting statistics too.

-------------------------------------------------------------------------------
File                        blank   comment      code    scale   3rd gen. equiv
-------------------------------------------------------------------------------
./MyWindowController.m         30        64        92 x   2.96 =         272.32
./CustomImageViewController.m  17        58        37 x   2.96 =         109.52
./AppDelegate.m                17        50        24 x   2.96 =          71.04
./CustomVideoViewController.m  15        46        18 x   2.96 =          53.28
./CustomTableViewController.m  13        44        17 x   2.96 =          50.32
./MyWindowController.h         13        40         9 x   2.96 =          26.64
./CustomImageViewController.h  13        40         8 x   2.96 =          23.68
./AppDelegate.h                12        40         7 x   2.96 =          20.72
./CustomVideoViewController.h  12        40         6 x   2.96 =          17.76
./CustomTableViewController.h  12        40         6 x   2.96 =          17.76
./main.m                       11        40         5 x   2.96 =          14.80
-------------------------------------------------------------------------------
SUM:                          165       502       229 x   2.96 =         677.84
-------------------------------------------------------------------------------

Here we see that most of the complexity (in lines-of-code terms anyway) resides in the window controller implementation: 92 lines of code, top of the list. That’s likely where one might expect greater complexity. But on the other hand, shouldn’t the view controller paradigm mitigate window controller complexity to some degree. We shall see.

Cash register

Let’s also use David A. Wheeler’s SLOCCount tool. It’s easy to install using MacPorts. Just enter sudo port install sloccount in Terminal. After installing, entering ”sloccount .” in the ViewController project directory gives:

Total Physical Source Lines of Code (SLOC)                = 246
Development Effort Estimate, Person-Years (Person-Months) = 0.05 (0.55)
 (Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05))
Schedule Estimate, Years (Months)                         = 0.17 (1.99)
 (Basic COCOMO model, Months = 2.5 * (person-months**0.38))
Estimated Average Number of Developers (Effort/Schedule)  = 0.28
Total Estimated Cost to Develop                           = $ 6,196
 (average salary = $56,286/year, overhead = 2.40).

Phew! 246 lines costing 6,000 dollars. Interesting that the number of lines do not agree. SLOCCount counts some of the comment lines. Big question is: can our experiment save bucks?

Let the games begin

Start with a new project. Use the Cocoa Application template: not document based, without Core Data. I’ll call it View Controller Mark II! That sounds Spitfire-esque. Excuse the appeal to glamour and (apparent) adventure. Just hope it doesn’t crash!

The new empty project provides just 5 lines of code in main.m. We have a blank MainMenu.xib too; just the main menu and main window. No application delegate, no window controller. Yet.

Rather than re-create the nibs, the experiment will just copy them from Apple’s sample code verbatim. That way, the comparison will be like-for-like. Only Objective-C code will change. Nothing else. So, delete MainMenu.xib; MainMenu.nib will replace it. Add to Resources: four Custom View nibs, MainMenu and TestWindow nibs. Add with Copy enabled. The custom video view uses QTMovieView so the project needs QTKit framework adding too. Add Quartz while we’re at it; the camera view uses Quartz’s QCView. The Lake Don Pedro JPEG image and QuickTime movie needs adding, finally.

Build already? No. It cannot run yet. Missing AppDelegate for one thing. After launch, the application delegate needs to create a new window controller (a custom one) and initialise it using the TestWindow nib.

So, we sub-class NSWindowController as MyWindowController. Add the necessary instance variables, myTargetView and viewController; add the (IBAction)viewChoicePopupAction:(id)sender instance method. That makes the project build and run, though do nothing remarkable of course.

By the time building and running reaches an error-free level of success, application delegate and window controller sources appear as follows.

Barebones application delegate

#import <Cocoa/Cocoa.h>

@class MyWindowController;

@interface AppDelegate : NSObject
{
    MyWindowController *myWindowController;
}
@end

It’s implementation:

#import "AppDelegate.h"
#import "MyWindowController.h"

@implementation AppDelegate

- (IBAction)newDocument:(id)sender
{
    // The MainMenu nib's main menu sends this action when you select File New,
    // or Command+N. The menu item target is the first responder. This falls
    // through the Responder Chain until it finally hits AppDelegate. Answer by
    // constructing the window controller, if not already existing, and show it.
    if (myWindowController == nil)
        myWindowController = [[MyWindowController alloc] initWithWindowNibName:@"TestWindow"];
    [myWindowController showWindow:self];
}

- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
    // By virtue of AppDelegate being the application's delegate, it
    // automatically receives application notifications. How kind. Respond by
    // sending the -newDocument: action, just as if the user pressed Command+N.
    [self newDocument:self];
}

@end

Barebones window controller

#import <Cocoa/Cocoa.h>

@interface MyWindowController : NSWindowController
{
    IBOutlet NSView *myTargetView;
    NSViewController *viewController;
        // TestWindow.nib contains bindings to this instance variable's "title"
        // and "representedObject" property. Rather than naming it
        // myCustomViewController, as does the Apple sample code, name the
        // instance variable by its KVC key. Doing so saves an accessor.
}

- (IBAction)viewChoicePopupAction:(id)sender;

@end

And its implementation:

#import "MyWindowController.h"

@implementation MyWindowController

- (IBAction)viewChoicePopupAction:(id)sender
{
    // TestWindow nib connects action -viewChoicePopupAction: to the window
    // controller. MyWindowController is the nib's owner. The
    // Image-Table-Video-Camera pop-up button sends this action to the nib
    // owner. The window controller responds by switching view and view
    // controller.
}

@end

At this point, then, the project has 40 lines of source code. Where to go from here?

Switching view controllers

That’s the main point. When the user selects one of the four choices available in the pop-up button (Image, Table, Video or iSight Camera) the custom view responds by switching to the corresponding view alternative. As regards complexity, meaning breadth and depth of view controller nesting, the requirement is very trivial. Breadth of four, depth of one. It’s hardly worth having any meta-control. But you have to start somewhere. Here’s as good a place as any.

To be continued.


View controllers

Posted by Pioneer! Tue, 26 Aug 2008 17:30:00 GMT

NSViewController. It’s an enigmatic class.

Exactly what is it? When should it be used? Apple’s documentation does not paint a clear picture. What is there to glean from its interface, implementation as well as others’ work on this subject?

Class diagram

The class belongs to the AppKit framework. Its simplified UML class diagram looks like this:

View Controller class diagram

It inherits from NSResponder, associates with a bundle, a view and some other represented object. That’s what the diagram says, in not so many words. Really though, the bundle association amounts to a nib association using a given nib name.

Title

What does the title attribute represent? An interesting question. Apple’s documentation proves most enigmatic. Described as the ‘localized title of the receiver’s view,’ the discussion continues:

NSViewController does not use the title property directly. This property is here because so many anticipated uses of this class will involve letting the user choose among multiple named views using a pulldown menu or some other user interface.”

Exactly what ‘anticipated uses’ refers to, the reader is left to wonder! The warm feeling of anticipation remains.

Usage

According to documentation, the view controller becomes the nib’s owner. Its view outlet connects to the nib’s primary view. Using Interface Builder’s Cocoa View template gives you such a nib. The class of File’s Owner defaults to NSObject. Change this to NSViewController and link its view outlet to Custom View—the NSView already set-up by the template. (Makes you wonder why the View template does not automatically make the file owner’s class equal to NSViewController with its view outlet pointing at the custom view.) Via the nib’s owner, the controller, views and controls within the nib can bind to some other key-value coding and observing (KVC and KVO) compliant object for key-path “representedObject” applied to the controller, i.e. nib owner.

Does that make any sense? It assumes some grasp of nib and key-value paradigms.

In simple terms, NSViewController ties together a view and a model, the represented object. Isn’t that exactly what we’d expect from a controller in MVC architecture? Superficially, the class is analogous to NSWindowController. What window controller does for window and document, view controller does for view and represented object: view and model, plus controller. Window controller has -loadWindow behaviour for loading the window from its nib. View controller analogously carries the -loadView method. It almost makes you wonder if the two classes should really be one and the same; or belong to the same super-class, an abstract window-view controller.

Exemplars

Studying examples is a good way to grasp new concepts. A picture paints a thousand words. Samples are software engineering pictures. Where best to find good paintings? Where better than Apple? Surprisingly though, Apple’s sample code contains only a few examples that use NSViewController. Why is that surprising? Anyway, here they are:

  • CrossEvents from 2007 subclasses the view controller. It demonstrates sending Carbon events and NSNotifications between Carbon and Cocoa. View controller’s usage is not the primary focus. It’s used just as a tool for loading a nib-based NSView. Not a complicated example, but helps to show that view controllers have their trivial use.
  • HIView-NSView—same can be said. This sample was upgraded in May 2007 to use NSViewController.
  • IconCollection—same again, not a direct demonstration of NSViewController usage per se. NSCollectionView’s use as an icon collection viewer is the main purpose. Yet it combines window and view controllers together with bindings.
  • SourceView is an interesting sample. Relatively complex, it combines custom view-controller subclasses, as well as custom window controller.
  • ViewController directly demonstrates view controlling. It has three custom view controllers, one each for image, table and video viewing. The sample also includes a window controller. So interesting to see how window and view controllers co-ordinate.

Are there any more? I’ve made a thorough search but no others at Apple Developer. Third-party developer sources may include examples, of course.

There is one intriguing fact about the Apple samples: only ViewController uses the represented object and only to store a integer representing the number of subviews present in the view controller’s view. So not really useful.

Responder and responder chain

NSViewController is a kind of NSResponder.

What is a responder anyway? If, like me, you generally ignore things that work right until they break, you’ll likely know little about the “responder chain,” how it works and why; simply because it normally doesn’t matter. The framework takes care of such details. Why concern yourself?

In this case, the answer is simple. NSViewController does not automatically join the responder chain, as does NSWindowController. If you want your view controllers in the chain, you must put them there yourself.

Respond to what and how?

The simplified story: responders (subclasses of NSResponder) link up together in a chain. When event or action messages fire, Apple’s Application Kit invokes the responders one-by-one until it finds a response. Events and actions correspond to keyboard presses, mouse clicks, scroll wheels, tablet events, menu items, help requests and such. Interactive inputs, by another name. Responders include windows, views and application objects, window and view controllers also.

That’s a very simplified version. See Apple’s Cocoa Event-Handling Guide for the gross details. Read the section entitled The Responder Chain.

In reality, the responder chain amounts to just a singly-linked list of NSResponder objects. Each responder encapsulates a nextResponder reference to the next responder in the chain, or nil if none. No next responder indicates the end of the chain. Every NSWindow has a first responder marking the start of the chain. However, the starting point varies; exact route varies too. Multiple responder chains exist at any one time, and which to choose depends on the type of message, whether or not NSWindowController is involved, and whether document or non-document architecture applies. So not exactly straightforward!

In all cases though, first responder is always first. Then comes the view hierarchy followed by the enclosing NSWindow and its delegate.

Question arises: where in the chain do view controllers belong? For instance, they could sit between the view hierarchy and its window. Alternatively, behind each view so that the view responds first followed immediately by its view controller. In practise, applications may need custom configurations of responders, possibly even dynamic.

More on this later.