Search code examples
swiftmacosappkitnscoding

Export Class with String & NSImage Properties to File


I have a class that I'd like to be able to open, and export to a file. It doesn't need to be edited - this is simply a way to share part objects CoreData database with other users of my app, so I don't think I need the complexity of subclassing NSDocument.

The class is pretty basic, with two properties String and a filename for a NSImage that references an image in the app sandbox.

Initially, I thought of using a Document Bundle, basically a folder with the images (if required) and a plist with the strings and image file names. Now, I'm leaning toward creating a class-just-for-export-and-import that uses NSCoding to save the images and the strings into a file.

Here's what I'm not sure about:

  • is this a good idea? Am I missing something here?
  • can I save an array of objects with NSImage and String properties into a single file? I know I can save an image, fairly easily, but can I bundle them up into a single document? Is NSCoding the way to do this?
  • Do I need to convert the NSImage to NSData first?
  • How does my decoder know which part of the file is a String and which part is a NSImage?

I have done my research! There's lots of info about saving an image, or saving a NSDocument, but I just want a simple way for users to export and import arbitrary data into, and out of, my application's core data store.


Solution

  • Using NSCoding you can save every class that has built-in support for this protocol or you add it. Both NSImage and – of course – NSString conforms to NSCoding. You simply have to add NSCoding support to your custom class, which is pretty simple:

    - (void)encodeWithCoder:(NSCoder*)coder
    {
      [coder encodeObject:self.stringProperty forKey:@"String Property"];
      [coder encodeObject:self.imageProperty forKey:@"Image Property"];
    }
    
    - (instancetype)initWithCoder:(NSCoder*)coder
    {
      self = [super init]; // or `-initWithCoder:, if it is supported
      if (self)
      {
        self.stringProperty = [coder decodeObjectForKey:@"String Property"];
        self.imageProperty = [coder decodeObjectForKey:@"Image Property"];
      }
      return self;
    }
    

    If you want to save your data as property list file, you solely can use property list classes. NSString is a property list, but NSImage is not – and of course your custom class is not. So you have to convert instances of NSImage and your custom class to instances of NSData, because NSData is a property list. You can do that with – drum-roll – NSCoding. Simply use a keyed archiver. The result of +archivedDataWithRootObject: (NSKeyedArchiver) is an instance of NSData. The way back you use NSKeyedUnarchiver.

    But here is little reason to chose the file format of property lists, if you do not want to edit it with generic property list tools.

    So, to answer directly to your Qs:

    is this a good idea? Am I missing something here?

    It's ok

    can I save an array of objects with NSImage and String properties into a single file?

    Since NSArray comforms to NSCoding and is a property list, both approaches will work out of the box.

    I know I can save an image, fairly easily, but can I bundle them up into a single document?

    You are the owner of the file and you define the file format. Just go ahead.

    Is NSCoding the way to do this?

    Yes.

    Do I need to convert the NSImage to NSData first?

    Only if you want to save a property list file.