This article begins by describing the Model-View-Controller (MVC) design pattern because this pattern informs application design that is most supportive of scripting, document-based applications, and undo. It does not fully describe the MVC design pattern in any formal way, because that’s not really its purpose, but it does discuss the pattern enough to give some background for the remaining discussion. More information about MVC is presented in the Cocoa Fundamentals Guide. In addition, Cocoa Bindings Programming Topics explains how you can use Cocoa bindings technology to keep model and view values synchronized.
The document architecture in the Application Kit is based on three classes: NSDocument
, NSWindowController
, and NSDocumentController
. NSDocument
is the principal class. It represents a single document in your application. Developers must subclass NSDocument
to give it knowledge of the application’s model layer and to implement persistence (loading and saving). NSWindowController
objects own and control the application’s user interface. An NSDocument
object has one or more NSWindowController
objects. Developers often subclass NSWindowController
to add specific knowledge of the view layer that the controller is responsible for managing. NSDocumentController
is a singleton class. Each document-based application has a single instance of NSDocumentController
to track and manage all open documents. Developers typically don’t need to subclass NSDocumentController
.
You can find more information about the document architecture in the Document-Based Applications Overview.
NSDocuments Are Model-Controllers
NSWindowControllers Are View-Controllers
Type Information and NSDocumentControllers
Typical Usage Patterns
Documents and Scripting
NSDocument
is a model-controller class. Its main job is to own and manage the model objects that make up a document and to provide a way of saving those objects to a file and reloading them later. Any and all objects that are part of the persistent state of a document should be considered part of that document’s model. Sometimes the NSDocument
object itself has some data that would be considered part of the model. For example, the Sketch example application has a subclass of NSDocument
named SKTDrawDocument
; objects of this class might have an array of SKTGraphic
objects that comprises the model of the document. In addition to the actual SKTGraphic
objects, the SKTDrawDocument
object contains some data that should technically be considered part of the model because the order of the graphics within the document’s array matters in determining the front-to-back ordering of the SKTGraphic
objects.
An NSDocument
object should not contain or require the presence of any objects that are specific to the application’s user interface. Although a document can own and manage NSWindowController
objects—which present the document visually and allow the user to edit it—it should not depend on these objects being there. For example, it might be desirable to have a document open in your application without having it visually displayed. For instance, a script might have opened a document to do some processing on it. If the script does not need the user to become involved in the processing, the script might want the document to be opened, manipulated, saved, and closed again, without it ever appearing onscreen.
NSWindowController
is a view-controller class. Its main job is to own and manage the view objects that are used to display and edit a document. A document that is visible to the user has one or more NSWindowController
objects to own and manage the visual presentation. Although you can use an NSWindowController
instance, most often you must subclass NSWindowController
to add specific knowledge of the interface. An NSWindowController
object usually gets its interface from a nib file. Subclasses often add outlets and actions for the controls and views within the nib file and the NSWindowController
object usually acts as the file’s owner for the nib.
In very simple cases where there is only one window for a document, you may want your NSDocument
class to have outlets and actions for the nib. In this case, the NSDocument
subclass acts as the file’s owner for the nib, but it still creates an NSWindowController
instance to own and manage the objects that are loaded from the nib. If you do choose to adopt this approach when quickly prototyping an application, you should be careful to localize the portions of your code that deal with the user interface, so you can later extract them from the document and put them into a custom window controller as your application becomes more complex.
An NSDocumentController
object manages documents. It keeps track of all open documents; it knows how to create new documents and how to open existing documents. It knows how to find open documents given either a window whose window controller refers to the document or the path of the file the document was loaded from. Developers typically won’t have to worry about what it does. NSDocumentController
knows how to read and use the metadata that a document-based application provides about the types of documents it can open. NSDocumentController
can provide information based on that metadata, such as lists of file types supported by an application and which NSDocument
subclasses are used for them.
All document-based applications declare information about the document types they support in the information property list (Info.plist
) of the application. Xcode provides an editor for creating and modifying this metadata. See the NSDocumentController
class specification for details on the Info.plist
keys required by the document architecture and how to include this metadata in your application project.
The metadata in the information property list declares the types of documents supported by an application. Cocoa defines a set of abstract types; these types are usually the same as the pasteboard type that represents such data. For each abstract type, the Info.plist
lists specific information such as:
The file extensions used to identify files of that type
The HFS four-letter type code for files of that type
The icon the Finder should use to display files of that type
The subclass of NSDocument
used by an application to deal with files of that type
NSDocumentController
loads all this type information and uses it. When NSDocumentController
runs an open panel it obtains the list of all file extensions for document types that your application can read; it passes that list to the open panel so that it can list the files that can be opened. When the user actually chooses a file to open, the NSDocumentController
object uses the metadata to identify the subclass of NSDocument
to use to create the document and load its data.
You can use the document architecture in three general ways. The following discussion starts with the simplest and proceeds 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, the developer needs 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. NSDocument
automatically creates an NSWindowController
instance to manage that nib file, but the NSDocument
object itself serves as the nib file’s 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 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
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 most simple cases.
If your document requires multiple windows (or allows multiple windows) on 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
may only create one NSWindowController
instance, 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 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; NSDocument
knows how to fill 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 that you duplicate work you would otherwise get for free. The TextEdit application project (distributed with Mac OS X) 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.
© 2001, 2006 Apple Computer, Inc. All Rights Reserved. (Last updated: 2006-08-07)