Every application that takes advantage of the document architecture must create at least one subclass of NSDocument
. This architecture requires that you override some NSDocument
methods (among several choices), and recommends overriding several others in certain situations. This article also includes advice to consider when overriding these and other NSDocument
methods, including init
and displayName
.
The functional areas described by items in the following subsections require you to override NSDocument
methods. You must override one reading and one writing method. In the simplest case, you can override the two data-based reading and writing methods described in the following subsection. If you need to deal with the location of the file, then you can override the URL reading and writing methods instead. If your application supports document files that are file packages, then you can override the file-wrapper reading and writing methods instead.
Note that these methods read and write entire files at once. If your application has a large data set, you may want instead to read and write pieces of your files at different times.
The dataOfType:error:
method may be overridden to create and return document data (packaged as an NSData
object) of a supported type, usually in preparation for writing that data to a file. The readFromData:ofType:error:
method may be overridden to convert an NSData
object containing document data of a certain type into the document’s internal data structures and display that data in a document window. The NSData
object usually results from the document reading a document file.
For applications that must be able to run on Mac OS X v10.3 and earlier, implement dataRepresentationOfType:
and loadDataRepresentation:ofType:
instead.
By default, the writeToURL:ofType:error:
method writes data to a file after obtaining the data from the fileWrapperOfType:error:
method, which gets it from the dataOfType:error:
method. The readFromURL:ofType:error:
method reads data from a file, creates an NSFileWrapper
object from it, and gives this object to the readFromFileWrapper:ofType:error:
method; if this object represents a simple file, it is passed to the readFromData:ofType:error:
method for processing; otherwise (if the object represents a directory), the readFromFileWrapper:ofType:error:
method can be overridden to handle the situation. Subclasses can override any of these methods instead of the data-based reading and writing methods if the way NSDocument
reads and writes document data is not sufficient; their override implementations, however, must also assume the loading duties of the data-based reading and writing methods.
For applications that must be able to run on Mac OS X v10.3 and earlier, implement writeToFile:ofType:
, fileWrapperRepresentationOfType:
, readFromFile:ofType:
, and loadFileWrapperRepresentation:ofType:
instead.
Don't invoke fileURL
(or fileName
, the method was that deprecated in favor if it, in Mac OS X v10.4), fileType
, or fileModificationDate
from within your overrides. During reading, which typically happens during object initialization, there is no guarantee that NSDocument
properties like the file's location or type have been set yet. Your overridden method should be able to determine everything it needs to do the reading from the passed-in parameters. During writing, your document may be asked to write its contents to a different location or using a different file type. Again, your overridden method should be able to determine everything it needs to do the writing from the passed-in parameters.
If your override cannot determine all of the information it needs from the passed-in parameters, consider overriding another method. For example, if you see the need to invoke fileURL
from within an override of readFromData:ofType:error:
, perhaps you should instead override readFromURL:ofType:error:
. For another example, if you see the need to invoke fileURL
from within an override of writeToURL:ofType:error:
, perhaps you should instead override writeToURL:ofType:forSaveOperation:originalContentsURL:error:
.
If your NSDocument
subclass uses an NSTextView
object to display its data, and it has an active NSUndoManager
object, then your document-writing method override (or another override called during document saving) should send the NSTextView
object a breakUndoCoalescing
message. Sending this message when saving the text view’s contents preserves proper tracking of unsaved changes and the document’s dirty state. For more information about supporting undo in a document-based application, see “How do I implement undo?.” Message flow during document saving is described in “Saving a Document.”
In Mac OS X v10.6 and later, NSDocument
has the ability to read documents concurrently, using background threads. A class method of NSDocument
, canConcurrentlyReadDocumentsOfType:
, gives you control over this capability. Your NSDocument
subclass can override this method to return YES
to enable loading of documents concurrently, using background threads. When this facility is enabled in this way, initWithContentsOfURL:ofType:error:
executes on a background thread when opening files via the Open panel or from the Finder. This allows concurrent reading of multiple documents and also allows the application to be responsive while reading a large document.
The default implementation of this method returns NO
. A subclass override should return YES
only for document types whose reading code can be safely executed concurrently, in non-main threads.
Code executed during the opening of a document triggered by an Apple event cannot get the current Apple event, because the event is suspended until all documents are read, to enable correct reporting of success or failure to the Apple event sender. So, for example, if you are checking the current Apple event for a search string, you should not enable concurrent document opening unless you have another solution for getting the search string.
The NSUndoManager
mechanism for automatically creating one group per UI event doesn't coexist very well with NSUndoManager
being used on non-main threads. So, you should disable undo registration during document reading, which is a good idea even in the absence of concurrency. For an example, see SKTDocument.m
in /Developer/Examples/Sketch
.
The functional areas described by items in the following bulleted list require method overrides in some situations.
Window controller creation
NSDocument
subclasses must also create their window controllers. They can do this indirectly or directly. If a document has only one nib file with one window in it, the subclass can override windowNibName
to return the name of the window nib file. As a consequence, a default NSWindowController
instance is created for the document, with the document as the nib file’s owner. If a document has multiple windows, or if an instance of a custom NSWindowController
subclass is to be used, the NSDocument
subclass must override makeWindowControllers
to create these objects.
Printing and page layout
Normally, a document-based application can change the information it uses to define how document data is printed. This information is encapsulated in an NSPrintInfo
object. If an application is to print document data, subclasses of NSDocument
must override printOperationWithSettings:error:
.
Applications that must be able to run on Mac OS X v10.3 and earlier should override printShowingPrintPanel:
instead.
If your application does not support printing, be sure to remove the printing-related menu items from the main menu nib provided by the Cocoa Document-based Application template in Xcode.
Making backup files
When it saves a document, NSDocument
creates a backup of the old file before it writes data to the new one. Backup files have the same name as the new file, but with a tilde just before the extension. Normally, if the write operation is successful, it deletes the backup file. Subclasses can override keepBackupFile
to return YES
and thus retain the most recent backup file.
Modifying the Save panel accessory view
By default, when NSDocument
runs the Save panel, and the document has multiple writable document types, it inserts an accessory view near the bottom of the panel. This view contains a pop-up menu of the writable types. If you don’t want this pop-up menu, override shouldRunSavePanelWithAccessoryView
to return NO
. You can also override prepareSavePanel:
to do any further customization of the Save panel.
Validating menu items
NSDocument
implements validateUserInterfaceItem:
to manage the enabled state of the Revert and Save As menu items. If you want to validate other menu items, you can override this method, but be sure to invoke the superclass implementation. For more information on menu item validation, see the description of the NSUserInterfaceValidations
protocol.
The initializers of NSDocument
are another issue for subclasses. The init
method is the designated initializer, and it is invoked by the other initializers initWithType:error:
and initWithContentsOfURL:ofType:error:
. If you need to perform initializations that must be done when creating new documents but should not be done when opening existing documents, override initWithType:error:
. If you have any initializations that apply only to documents that are opened, you should override initWithContentsOfURL:ofType:error:
. If you have general initializations, you should, of course, override init
. In both cases, be sure to invoke the superclass implementation as the first thing.
If your application must be able to run on Mac OS X v10.3 and earlier, you can override initWithContentsOfFile:
or initWithContentsOfURL:
instead.
If you override init
, make sure that your override never returns nil
. Doing so could cause a crash (in some versions of the Application Kit) or presentation of a less than useful error message. If, for example, you want to prevent the creation or opening of documents under circumstances unique to your application, override a specific NSDocumentController
method instead.
Subclasses of NSDocument
sometimes override displayName
to customize the titles of windows associated with the document. That is usually not the right thing to do because the document's display name is used in places other than the window title, and the custom value that an application might want to use as a window title is often not appropriate. For example, the document display name is used in the following places:
Error alerts that may be presented during reverting, saving, or printing of the document
Alerts presented during document saving if the document has been moved, renamed, or put in the trash
The alert presented when the user attempts to close the document with unsaved changes
As the default value shown in the "Save As:" field of Save panels.
To customize a document's window title properly, subclass NSWindowController
and override windowTitleForDocumentDisplayName:
. If your application requires even deeper customization, override synchronizeWindowTitleWithDocumentName
.
Last updated: 2010-03-24