One of the advantages of using document packages is that you do not need to write everything each time you save. You should add a documentFileWrapper
property to your NSDocument
subclass and, when the document architecture reads in a document package, you should store an NSFileWrapper
reference in that property, and you should also keep track of what has been changed. For example,aAssuming you’re keeping a text file and a video file in your document package:
@interface MyDocument ()
@property (nonatomic, copy) NSString *text;
@property (nonatomic, assign) bool textHasChanged;
@property (nonatomic, copy) NSData *videoData;
@property (nonatomic, assign) bool videoHasChanged;
@property (nonatomic, strong) NSFileWrapper *documentFileWrapper;
@end
and
- (BOOL)readFromFileWrapper:(NSFileWrapper *)fileWrapper
ofType:(NSString *)typeName
error:(NSError **)outError {
// Read the contents of file wrapper
// …
[self setDocumentFileWrapper:fileWrapper];
return YES;
}
If a document package was read from disk, documentFileWrapper
keeps a reference to that package. If it’s a new document, documentFileWrapper
is nil
.
When saving the document, you reuse documentFileWrapper
and write only the files that need saving. In case the document hasn’t been saved yet (it’s a new document), documentFileWrapper
is nil
, so you need to create one:
- (NSFileWrapper *)fileWrapperOfType:(NSString *)typeName
error:(NSError **)outError
{
if ([self documentFileWrapper] == nil) {
NSFileWrapper *documentFileWrapper = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:@{}];
[self setDocumentFileWrapper:documentFileWrapper];
}
NSDictionary *fileWrappers = [[self documentFileWrapper] fileWrappers];
NSFileWrapper *textFileWrapper = [fileWrappers objectForKey:@"text.txt"];
NSFileWrapper *videoFileWrapper = [fileWrappers objectForKey:@"video.mp4"];
if ((textFileWrapper == nil && [self text] != nil) || [self textHasChanged]) {
if (textFileWrapper != nil)
[documentFileWrapper removeFileWrapper:textFileWrapper];
if ([self text] != nil) {
NSData *textData = [[self text] dataUsingEncoding:NSUTF8StringEncoding];
[documentFileWrapper addRegularFileWithContents:textData preferredFileName:@"text.txt"];
}
}
if ((videoFileWrapper == nil && [self videoData] != nil) || [self videoHasChanged]) {
if (videoFileWrapper != nil)
[documentFileWrapper removeFileWrapper:videoFileWrapper];
if ([self videoData] != nil)
[documentFileWrapper addRegularFileWithContents:[self videoData] preferredFileName:@"video.mp4"];
}
return documentFileWrapper;
}
In the code above, the document package (file wrapper) is created only if it doesn’t already exist. In other words, it is created only if the document is new and hasn’t been saved yet. After the package is created, its reference is stored in documentFileWrapper
.
Next, we check whether we need to write the text file. It is written if:
* either there is no text file yet (probably because it’s a new package) and we have actual text to write;
* or if the text has been changed.
We do the same for the video file.
Note that if the document has been loaded from disk or saved at least once, the video file is added to the package (and the previous video file, if any, is removed from the package) only if the video data has been changed.
NB: this answer assumes ARC and automatic property synthesis (ivar names prefixed with an underscore).