Instances of the three major classes in the document architecture play distinct but cooperating roles, as described in this article.
The primary job of an application’s NSDocumentController
object is to create and open documents, and to track and manage those documents. When a user chooses New from the File menu, an NSDocumentController
object gets the appropriate NSDocument
subclass from the CFBundleDocumentTypes
property in the application’s information property list, allocates an instance of this class, and initializes this instance by invoking the NSDocument
method initWithType:error:
. When the user chooses Open from the File menu, the NSDocumentController
object displays the Open panel, gets the user’s selection, finds the NSDocument
subclass for the file (based on its document type information), allocates an instance of this class, and initializes the object and loads document data by invoking the NSDocument
method initWithContentsOfURL:ofType:error:
. In both cases, the NSDocumentController
object adds a reference to the document object to an internal list to facilitate the management of its documents. It has a notion of the current document as the document whose window is currently main. (The foremost application window that is the focus of the user’s attention is called the main window.)
The NSDocumentController
object manages the Open Recent menu for the application, maintaining a list of the URLs of documents the application has recently handled. It notes recent documents during the opening, saving, reverting, and closing of documents.
NSDocumentController
is hard-wired to respond appropriately to certain application events, such as when the application starts up, when it terminates, when the system is shutting down, and when documents are opened or printed from the Finder. If you wish, you can make a custom object the application delegate and implement the delegate methods invoked as a result of the same events, and these methods will be invoked instead. However, the default NSDocumentController
object is an adequate application controller for most situations, and typically you should not need to subclass it. If you require additional behavior, such as displaying About panels and handling application preferences, you can have a custom controller object perform these duties rather than a subclass of NSDocumentController
. Nonetheless, it is possible to subclass NSDocumentController
if necessary, as described in “Creating a Subclass of NSDocumentController.”
The primary job of an NSDocument
object is to represent, manipulate, store, and load the persistent data associated with a document. As such it is a model-controller. Based on the document types it claims to understand (as specified in the CFBundleDocumentTypes
property of the application’s information property list), a document must be prepared to:
Provide the data displayed in windows (and represented internally) in a supported document type.
Store document data in a file at a specified location in the file system.
Read document data stored in a file.
With the assistance of its window controllers, an NSDocument
object manages the display and capture of the data in its windows. By some special hard-wiring of the Application Kit, the NSDocument
object associated with the key window is the recipient of first-responder action messages when users save, print, revert, and close documents. In response to the appropriate action, it knows how to run and manage the Save panel and the Page Layout panel.
A fully implemented NSDocument
object knows how to track its edited status, print document data, and perform undo and redo operations. Although these behaviors aren’t completely provided by default, the NSDocument
object does assist the developer in implementing each. For edited-status tracking, the NSDocument
object provides an API for updating a change counter. For undo/redo operations, the NSDocument
object by default lazily creates an NSUndoManager
instance when one is requested, responds appropriately to Undo and Redo menu commands, and updates the change counter when undo and redo operations are performed. For printing, the NSDocument
object facilitates the display of the Page Layout panel and the subsequent modification of the NSPrintInfo
object used in printing.
An NSWindowController
object manages one window associated with a document, which is usually stored in a nib file. As such it is a view-controller. If a document has multiple windows, each window has its own window controller. For example, a document might have a main data-entry window and a window listing records for selection; each window would have its own NSWindowController
object. When requested by its owning NSDocument
object, an NSWindowController
object loads the nib file containing a window and displays it. It also assumes responsibility for closing windows properly (after ensuring that the data they display is saved).
The NSWindowController
class offers additional behavior to document-based applications, such as cascading document windows in relation to each other, so they don’t completely obstruct one another.
Subclasses of NSWindowController
are optional. Applications can often use the default instance. Subclasses can augment NSWindowController
to perform different nib-loading and setup tasks or to customize the titles of windows.
This section describes three ways to use the document architecture, starting with the simplest and proceeding to the most complex.
The simplest way to use the document architecture is appropriate for documents that have only one window and are simple enough that there isn’t much benefit in splitting the controller layer into a model-controller and a view-controller. In this case, you need only to create a subclass of NSDocument
. The NSDocument
subclass provides storage for the model and the ability to load and save document data. It also has any outlets and actions required for the user interface. It overrides windowNibName
to return the nib file name used for documents of this type. The NSDocument
object automatically creates an NSWindowController
object to manage that nib file, but the NSDocument
object serves as the nib file's owner.
If your document has only one window, but it is complex enough that you’d like to split up some of the logic in the controller layer, you can subclass NSWindowController
as well as NSDocument
. In this case, any outlets and actions and any other behavior that is specific to the management of the user interface goes into the NSWindowController
subclass. Your NSDocument
subclass must override makeWindowControllers
instead of windowNibName
. The makeWindowControllers
method should create an instance of your NSWindowController
subclass and add it to the list of managed window controllers with addWindowController:
. The NSWindowController
object should be the file’s owner for the nib file because this creates better separation between the view-related logic and the model-related logic. This approach is recommended for all but the simplest cases.
If your document requires or allows multiple windows for a single document, you should subclass NSWindowController
as well as NSDocument
. In your NSDocument
subclass you override makeWindowControllers
just as in the second procedure described above. However, in this case you might create more than one instance of NSWindowController
, possibly from different subclasses of NSWindowController
. Some applications need several different windows to represent one document. Therefore you probably need several different subclasses of NSWindowController
and you must create one of each in makeWindowControllers
. Some applications need only one window for a document but want to allow the user to create several copies of the window for a single document (sometimes this is called a multiple-view document) so that the user can have each window scrolled to a different position or displayed in different ways. In this case, your makeWindowControllers
override may create only one NSWindowController
object, but there will be a menu command or similar control that allows the user to create others.
Scripting support is mostly automatic for applications based on the document architecture, for several reasons. First, NSDocument
and the other classes in the document architecture directly implement the standard document scripting class (as expected by AppleScript) and automatically support many of the scripting commands that apply to documents. Second, because the document architecture is intended to work with application designs that use model-view-controller (MVC) separation, and because scripting support depends on many of the same design points, applications that use the document architecture are already in better shape to support scripting than other applications that are not designed that way. Finally, the document plays an important role in the scripting API of most applications; the NSDocument
class fills that role and provides a good starting point for allowing scripted access to the model layer of your application.
If an application is not based on the document architecture, making it scriptable requires you to duplicate work you would otherwise get for free. The TextEdit example application shows how to make a document-based application that is not based on NSDocument
scriptable. See the Sketch example project for an example of how to implement a scriptable NSDocument
-based application.
Last updated: 2010-03-24