Load and Save

As a document-base application, it has to be able to load and save file. Depending on the structure of the files, I have several choices to override in NSDocument:

Table 13.1. Methods for loading and saving files

 Load fileSave file
High levelloadDataRepresentation:ofType:dataRepresentationOfType:
Middle levelloadFileWrapperRepresentation:ofType:fileWrapperRepresentationOfType:
Low levelreadFromFile:ofType:, readFromURL:ofType:writeToFile:ofType:, writeToURL:ofType:

Read How do I implement saving and loading for simple files?, How do I implement document packages (documents that are really folders, but appear to be opaque documents)?, and How do I implement loading and saving when the simple data or file wrapper API won't do?.

In this case, -loadDataRepresentation:ofType: and -dataRepresentationOfType: are enough.

Document.m:

- (NSData *) dataRepresentationOfType: (NSString *) type
{
   if (type == nil)
      type = @"mon";
   if ([type isEqualToString: @"mon"])
      return [NSArchiver archivedDataWithRootObject: records];
   else
      return nil;
}

- (BOOL) loadDataRepresentation: (NSData *) data ofType: (NSString *) type
{
   if ([type isEqualToString: @"mon"])
      {
         [records setArray: [NSUnarchiver unarchiveObjectWithData: data]];
         return YES;
      }
   return NO;
}

When document is going to be saved, it will call -dataRepresentationOfType:. The type will be the NSName in the property list of this application (MoneyInfo.plist). Here, I use NSArchiver to transform the whole NSArray into NSData, and I don't need to worry about the internal format. When document is going to be loaded, it will call -loadDataRepresentation:ofType:. Again, I use NSUnarchiver to transform the NSData into NSArray. That's all. If you are using your own data structure, you have to deal with data archives by yourself. Here is a related article: Using the Property List Objects and the NSCoding Protocol.

The saved data is in binary format, which is usually undesired. Since I only use the basic data structure, I could save it into property list, which is human-readable.

Document.m:

- (NSData *) dataRepresentationOfType: (NSString *) type
{
   if (type == nil)
      type = @"mon";
   if ([type isEqualToString: @"mon"])
      return [[records description] dataUsingEncoding: [NSString defaultCStringEncoding]];
   else
      return nil;
}

- (BOOL) loadDataRepresentation: (NSData *) data ofType: (NSString *) type
{
   if ([type isEqualToString: @"mon"])
      {
        NSString *string = [[NSString alloc] initWithData: data 
                                                 encoding: [NSString defaultCStringEncoding]];
        [records setArray: [string propertyList]];
        RELEASE(string);
        return YES;
      }
   return NO;
}

I use -description to get the property list, which is NSString. Then use -dataUsingEncoding to transform property list (NSString) into NSData for saving. And do the opposite for the loading. Now, you can look at the saved file, which is very easy to read.

Here is the source code for the easy-read version: LoadSave-src.tar.gz.

If you are saving the document into a directory, such as HTML document, which contain text and images, you may want to use -loadFileWrapperRepresentation:ofType: and -fileWrapperRepresentationOfType:. Methods -readFromFile:ofType: and -writeToFile:ofType: offers the ability to access the file system directly.