Search code examples
objective-ccocoaxcode4

How do I use a subclass of NSDocumentController in Xcode 4?


I am currently in the process of trying to teach myself Cocoa development. Toward this end, I purchased a mostly-excellent book, Cocoa Recipes for Mac OS X: Vermont Recipes, which walks through creating a sample application. It's pretty good, but it's written against XCode 3.2 rather than XCode 4. So far, I've been able to bridge this myself, but I've run up against an issue where I can't figure out how to follow the instruction.

Essentially, the book goes through a sample case of subclassing NSDocumentController so that the application can handle two (eventually maybe an arbitrary number) different types of documents, and so it opens the appropriate window for each type. So, I've created a custom subclass of NSDocumentController (which the book calls VRDocumentController), and now I need to make it such that an instance of this controller loads relatively early in the application launch process. Basically, the class is a singleton, and so I have to instantiate mine before the application instantiates the standard class, and this has to be done early in the process. Fair enough.

The book cites the Apple documentation for subclassing NSDocumentController, which states that there are two ways to attack the problem: to instantiate the class in your MainMenu.xib file or to instantiate one in your -applicationWillFinishLaunching: delegate method. The Apple documentation doesm't give clear instruction on how to do either of these (more on that in a moment) and the book covers only the first version, which I think is probably my preferred method.

My problem: I cannot for the life of me pull this off in XCode 4. The instructions the book provides for XCode 3.2 are no longer accurate, because Interface Builder has been shuffled into XCode itself now and the new version of the "classes tab" no longer shows my project's classes. I found this question on Stack Overflow asking a similar question, so I tried following the accepted answer there. However, when I open the Identity Inspector and try to type VRDocumentController, it just beeps at me and doesn't take it. None of the other controller classes I've written seem to be acceptable inputs either.

I'd also be happy to go the other route; instantiating a copy in the -applicationWillFinishLaunching method. But, I have no earthly idea in which class that method actually belongs, or what its return type is. I've done a non-trivial amount of searching for that, too, with no luck.


Solution

  • In your application delegate:

    // LukeAppDelegate.h
    #import "LukeAppDelegate.h"
    #import "VRDocumentController"
    
    - (void)applicationWillFinishLaunching:(NSNotification *)notification {
        VRDocumentController *dc = [[VRDocumentController alloc] init];
    }
    

    This will make sure that an instance of VRDocumentController is created and registered as the shared document controller, preventing Cocoa from using the default NSDocumentController.

    As to why you haven’t been able to use a custom object in your nib file, make sure that that you select Object (blue cube) instead of Object Controller (blue cube inside a green sphere) when dragging a new object into the nib file.


    Edit: If you’re targeting an OS X version that supports restoration, -applicationWillFinishLaunching: may be too late to register a custom document controller. If the application delegate is placed inside MainMenu.xib, it should be instantiated by the nib loading process before any documents are restored, hence you can move the NSDocumentController subclass initialisation to the application delegate’s init method:

    // LukeAppDelegate.h
    #import "LukeAppDelegate.h"
    #import "VRDocumentController"
    
    - (id)init {
        self = [super init];
        VRDocumentController *dc = [[VRDocumentController alloc] init];
        return self;
    }