An abstract base class for managing the data of documents.
- iOS 5.0+
Applications that make use of
UIDocument and its underlying architecture get many benefits for their documents:
Asynchronous reading and writing of data on a background queue. Your application's responsiveness to users is thus unaffected while reading and writing operations are taking place.
Coordinated reading and writing of document files that is automatically integrated with cloud services.
Support for discovering conflicts between different versions of a document (if that occurs).
Safe-saving of document data by writing data first to a temporary file and then replacing the current document file with it.
Automatic saving of document data at opportune moments; this mechanism includes support for dealing with suspend behaviors.
In the Model-View-Controller design pattern, a
UIDocument object is a model object or model-controller object—it manages the data of a document or the aggregate model objects that together constitute the document's data. You typically pair it with a view controller that manages the view presenting the document’s contents.
UIDocument provides no support for managing document views.
Document-based applications include those that can generate multiple documents, each with its own file-system location. A document-based application must create a subclass of
UIDocument for its documents. See “Subclassing Notes,” below, for details.
The primary attribute of a document in the
UIDocument architecture is its file URL. When you initialize an instance of your document subclass by calling
init(fileURL:), you must pass a file URL locating the document file in the application sandbox.
UIDocument determines the file type (the Uniform Type Identifier associated with the file extension) and the document name (the filename component) from the file URL. You can override the accessor methods of the
localizedName properties to supply different values.
The following outlines the life cycle of a typical document (see “Subclassing Notes” for implementation details):
You create a new document or open an existing document.
To create a new document, allocate and initialize an instance of your subclass and then call
save(to:for:completionHandler:)on the instance.
To open an existing document (selected by the user), allocate and initialize an instance of your subclass and then call
open(completionHandler:)on the instance.
The user edits the document.
As the user edits, track changes to the document.
UIDocumentperiodically notes when there are unsaved changes and writes the document data to its file.
The user requests that the document to be integrated with cloud services (optional).
You must enable the document for cloud storage. You must also resolve any conflicts between different versions of the same document.
The user closes the document.
close(completionHandler:)on the document instance.
UIDocumentsaves the document if there are any unsaved changes.
A typical document-based application calls
save(to:for:completionHandler:) on the main thread. When the read or save operation kicked off by these methods concludes, the completion-handler block is executed on the same dispatch queue on which the method was invoked, allowing you to complete any tasks contingent on the read or save operation. If the operation is not successful,
false is passed into the completion-hander block.
Implementation of the NSFilePresenter Protocol
UIDocument class adopts the
NSFilePresenter protocol. When another client attempts to read the document of a
UIDocument-based application, that reading is suspended until the
UIDocument object is given an opportunity to save any changes made to the document.
Although some implementations do nothing,
UIDocument implements all
NSFilePresenter methods, . Specifically,
relinquishPresentedItem(toWriter:)to check if the file-modification date has changed; if the file is newer than before, it calls
revert(toContentsOf:completionHandler:)with the value of the
fileURLas the URL parameter.
presentedItemDidMove(to:)to update the document’s file URL (
UIDocument subclass, if you override a
NSFilePresenter method you can always invoke the superclass implementation (
Each document-based application must create a subclass of
UIDocument whose instances represent its documents. The subclassing requirements for most applications are simple:
For writing operations, implement the
contents(forType:)method to provide a snapshot of document data. The data must be in the form of an
NSDataobject (for flat files) or an
FileWrapperobject (for file packages). Writing operations are usually initiated through the autosave feature
For reading operations, implement the
load(fromContents:ofType:)method to receives an
FileWrapperobject and initialize the application's data structures with it.
Implement change tracking to enable the autosaving feature. See Change Tracking for details.
When cloud services are enabled for a document, resolve conflicts between different versions of a document. See Conflict Resolution and Error Handling for details.
load(fromContents:ofType:) methods are typically called on the main queue. More specifically:
contents(forType:)method is called on the queue that the
save(to:for:completionHandler:)method was called on; writing of data takes place on a background thread
load(fromContents:ofType:)method is called on the queue that the
open(completionHandler:)method was called on
If you have special requirements for reading and writing document data for which the
load(fromContents:ofType:) methods won’t suffice, you can override other methods of the
UIDocument class. See Advanced Overrides for a discussion of these requirements and methods.
To enable the autosaving feature of
UIDocument, you must notify it when users make changes to a document.
UIDocument periodically checks whether the
hasUnsavedChanges method returns
true; if it does, it initiates the save operation for the document.
There are two primary ways to implement change tracking in your
Call the methods of the
UndoManagerclass to implement undo and redo for the document. You can access the default
UndoManagerobject from the
undoManagerproperty. This is the preferred approach, especially for existing applications that already support undo and redo.
updateChangeCount(_:)method at the appropriate junctures in your code.
Conflict Resolution and Error Handling
UIDocument object has a specific state at any moment in its life cycle. You can check the current state by querying the
documentState property, and get notified about changes by observing the
If a document is enabled for iCloud, it is important to check for conflicting versions and to attempt to resolve conflicts. Do this by listening for the
UIDocumentStateChanged notification and then checking if the document state is
inConflict. This state indicates that there are conflicting versions of the document, which you can access by calling the
NSFileVersion class method
unresolvedConflictVersionsOfItem(at:), passing in the document’s file URL. If you can resolve a conflict correctly without user interaction, do so. Otherwise, discretely notify the user that a conflict exists and let them choose how to resolve it. Possible approaches include:
Displaying the conflicting versions, from which a user can pick one or both versions to keep
Displaying a merged version and giving the user an option to pick it
Displaying the file modification dates and giving the user the option to choose one or both
Document state, in addition to indicating an inter-file conflict, can indicate errors. For example,
closed indicates an error in reading, and
savingError indicates an error in saving or reverting a document. Your app is notified of reading and writing errors through the
success parameter passed into the completion handlers of the
You can handle errors by calling or implementing the
handleError(_:userInteractionPermitted:) method; this method is called by the default implementations of the
save(to:for:completionHandler:) methods when a
UIDocument object encounters a reading or writing error, respectively. You can handle read, save, and reversion errors by informing the user and, if the situation permits, trying to recover from the error.
Be sure to read the description for the
contents(forType:) method for its guidance on handling errors encountered during document saving.
If you application has special requirements for reading or writing document data, it can override methods of
UIDocument other than
contents(forType:). These requirements often include the following:
Incremental reading and writing of large data files
Custom representations of document data (that is, not a
Performing actions before or after reading or writing data
A custom approach to safe-saving
Changing the file type of a document before it’s saved
savingFileTypemethod to return a file type other than the default (
fileType). An example of this is an RTF document which, after a user adds an image to it, should be saved as an RTFD document.
If you override most of these methods be aware that all reading and writing of document data must be done on a background queue and must be coordinated with other attempts to read from and write to the same document file. Because of this, you should usually call the superclass implementation (
super) as part of your override, and if you call other
UIDocument methods you should usually invoke them in the block passed into a call of the
performAsynchronousFileAccess(_:) method. Read the method descriptions for the details.
Thread Safety Considerations
If you override any of the document-attribute properties (listed under Accessing Document Attributes) by overriding the related accessor methods, be aware that the UIKit framework can call these accessor methods on a background thread. Thus your overriding implementation must be thread safe.