< Previous PageNext Page > Hide TOC

Document Architecture

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.

Contents:

NSDocuments Are Model-Controllers
NSWindowControllers Are View-Controllers
Type Information and NSDocumentControllers
Typical Usage Patterns
Documents and Scripting


NSDocuments Are Model-Controllers

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.

NSWindowControllers Are View-Controllers

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.

Type Information and NSDocumentControllers

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:

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.

Typical Usage Patterns

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.

Documents and Scripting

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.



< Previous PageNext Page > Hide TOC


© 2001, 2006 Apple Computer, Inc. All Rights Reserved. (Last updated: 2006-08-07)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.