Using FileWrappers as File Containers
NSFileWrapper instance holds a file’s contents in dynamic memory. In this role it enables a document object to embed a file, treating it as a unit of data that can be displayed as an image (and possibly edited in place), saved to disk, or transmitted to another app. It can also store an icon for representing the file in a document or in a dragging operation.
Instances of this class are referred to as file wrapper objects, and when no confusion will result, merely as file wrappers. A file wrapper can be one of three specific types: a regular file wrapper, which holds the contents of a single actual file; a directory wrapper, which holds a directory and all of the files or directories within it; or a link wrapper, which simply represents a symbolic link in the file system.
Because the purpose of a file wrapper is to represent files in memory, it’s very loosely coupled to any disk representation. A file wrapper doesn’t record the path to the disk representation of its contents. This allows you to save the same file wrapper with different URLs, but it also requires you to record those URLs if you want to update the file wrapper from disk later.
Working with File Wrappers
You can create a file wrapper from data in memory using the
initWithSerializedRepresentation: method or from data on disk using the
initWithURL:options:error: method. Both create the appropriate type of file wrapper based on the nature of the serialized representation or of the file on disk.
Three convenience methods each create a file wrapper of a specific type:
initSymbolicLinkWithDestination:. Because each initialization method creates file wrappers of different types or states, they’re all designated initializers for this class—subclasses must meaningfully override them all as necessary.
Some file wrapper methods apply only to a specific wrapper type, and an exception is raised if a method sent to a file wrapper of the wrong type. To determine the type of a file wrapper, use the
A file wrapper stores file system information (such as modification time and access permissions), which it updates when reading from disk and uses when writing files to disk. The
fileAttributes method returns this information in the format described in the
attributesOfItemAtPath:error:. You can also set the file attributes using the
NSFileWrapper class allows you to set a preferred filename for save operations, and it records the last filename it was actually saved to; the
filename methods return these names. This feature is most important for directory wrappers, though, and so is discussed under “Working with Directory Wrappers.”
When saving a file wrapper to disk, you typically determine the directory you want to save it in, then append the preferred filename to that directory URL, and use the
writeToURL:options:originalContentsURL:error: method, which saves the file wrapper’s contents and updates the file attributes. You can save a file wrapper under a different name if you like, but this doing so may result in the recorded filename differing from the preferred filename, depending on how you invoke the
Besides saving its contents to disk, a file wrapper can reread them from disk when necessary. The
matchesContentsOfURL: method determines whether a disk representation may have changed, based on the file attributes stored the last time the file was read or written. If the file wrapper’s modification time or access permissions are different from those of the file on disk, this method returns
YES. You can then use
readFromURL:options:error: to re-read the file from disk.
Finally, to transmit a file wrapper to another process or system (for example, using the pasteboard), you use the
serializedRepresentation method to get an
NSData object containing the file wrapper’s contents in the
NSFileContentsPboardType format. You can safely transmit this representation over whatever channel you choose. The recipient of the representation can then reconstitute the file wrapper using the
Working with Directory Wrappers
A directory wrapper contains other file wrappers (of any type), and allows you to access them by keys derived from their preferred filenames. You can add any type of file wrapper to a directory wrapper with the
addFileWrapper: method and remove it with the
removeFileWrapper: method. The convenience methods
addSymbolicLinkWithDestination:preferredFilename: allow you to add regular file and link wrappers while also setting their preferred names.
A directory wrapper stores its contents in an
NSDictionary object, which you can retrieve using the
fileWrappers method. The keys of this dictionary are based on the preferred filenames of each file wrapper contained in the directory wrapper. There exist, then, three identifiers for a file wrapper within a directory wrapper:
Preferred filename. This identifier doesn’t uniquely identify the file wrapper, but the other identifiers are always based on it.
Dictionary key. This identifier is always equal to the preferred name when there are no other file wrappers of the same preferred name in the same directory wrapper. Otherwise, it’s a string made by adding a unique prefix to the preferred filename. Note that the same file wrapper can have a different dictionary key for each directory wrapper that contains it. You use the dictionary key to retrieve the file wrapper object in memory, in order to get its contents or its filename (that is, to update it from disk). You can get a file wrapper’s dictionary key by sending a
keyForFileWrapper:message to the directory wrapper that contains it.
Filename. This identifier is usually based on the preferred filename, but isn’t necessarily the same as it or the dictionary key. You use the filename to update a single file wrapper relative to the path of the directory wrapper that contains it. Note that the filename may change whenever you save a directory wrapper containing the file wrapper. Particularly if the file wrapper has been added to several different directory wrappers. Thus, you should always retrieve the filename from the file wrapper itself each time you need it rather than caching it.
When working with the contents of a directory wrapper, you can use a dictionary enumerator to retrieve each file wrapper and perform whatever operation you need. With the exceptions of saving and updating, a directory file wrapper defines no recursive operations for its contents. To set the file attributes for all contained file wrappers, or to perform any other such operation, you must define a recursive method that examines the type of each file wrapper and invokes itself again for any directory wrapper it encounters.