How do I save additional content into my UIManagedDocument file packages?
Asked Answered
L

2

11

I'm having a lot of trouble deciphering Apple's documentation around UIManagedDocument, specifically the following methods:

  • - (id)additionalContentForURL:(NSURL *)absoluteURL error:(NSError **)error
  • - (BOOL)readAdditionalContentFromURL:(NSURL *)absoluteURL error:(NSError **)error
  • - (BOOL)writeAdditionalContent:(id)content toURL:(NSURL *)absoluteURL originalContentsURL:(NSURL *)absoluteOriginalContentsURL error:(NSError **)error

Has anyone successfully managed to save additional content into the "addition content" directory inside their UIManagedDocument packages? I'm looking to save straight images (PNGs, JPEGs, etc) and videos (m4v, etc) into this directory using UUIDs as the filenames (with the correct file extension), and storing references to these individual files as NSString file paths within my persistent store.

Lagrange answered 2/12, 2011 at 4:54 Comment(1)
Have you had any luck with this? I'm looking into the same thing.Leid
L
12

Credit goes to Apple DTS for helping me understand this class. I'm sharing some of the example they helped me with here (modified slightly).

OK, so basically it works like this: subclass UIManagedDocument, and implement the following methods (where the extraInfo property is just an NSDictionary implemented on our subclass):

- (BOOL)readAdditionalContentFromURL:(NSURL *)absoluteURL error:(NSError **)error
{
   NSURL *myURL = [absoluteURL URLByAppendingPathComponent:@"AdditionalInformation.plist"];
   self.extraInfo = [NSDictionary dictionaryWithContentsOfURL:myURL];
   return YES;
}

- (id)additionalContentForURL:(NSURL *)absoluteURL error:(NSError **)error
{
   if (!self.extraInfo) {
       return [NSDictionary dictionaryWithObjectsAndKeys: @"Picard", @"Captain", [[NSDate date] description], @"RightNow", nil];
   } else {
       NSMutableDictionary *updatedFriendInfo = [self.extraInfo mutableCopy];
       [updatedFriendInfo setObject:[[NSDate date] description] forKey:@"RightNow"];
       [updatedFriendInfo setObject:@"YES" forKey:@"Updated"];

       return updatedFriendInfo;
   }
}

- (BOOL)writeAdditionalContent:(id)content toURL:(NSURL *)absoluteURL originalContentsURL:(NSURL *)absoluteOriginalContentsURL error:(NSError **)error
{
   if (content) {
       NSURL *myURL = [absoluteURL URLByAppendingPathComponent:@"AdditionalInformation.plist"];
       [(NSDictionary *)content writeToURL:myURL atomically:NO];
   }

   return YES;
}

UIManagedDocument will call these methods when it needs to, automatically saving whatever you need to save to the document package inside an AdditionalContent directory.

If you need to force a save, simply call the following on your UIManagedDocument instance:

[self updateChangeCount:UIDocumentChangeDone];

At present, I'm not using this for images and videos — but the example should give you enough to go off.

Lagrange answered 17/1, 2012 at 21:28 Comment(5)
Great, thanks for this! The only thing I'm unsure about is the handling of files (for example photos, mp3s, etc) that will be attached to the document. Is that all done manually by coordinating file access into the additional content directory of the document's file wrapper?Leid
So basically the way I was going to handle this was to read/write NSURLs of the arbitrary files into an array attached to the UIManagedDocument subclass. When the readAdditionalContentFromURL:error: method is called, get a list of the URLs on disk and store that. You would probably need to associate some form of cache with the subclass that you flushed whenever writeAdditionalContent:toURL:originalContentsURL:error: is called to actually write the photos/mp3s/et al to disk. You might choose to store the files in a temporary directory until this flush occurs.Lagrange
I couldn't see a very clean way to do this, so given that I am targeting iOS 5 and higher for this project I enabled external storage of binary properties in my Core Data store, and I push the images and videos into my persistent store rather than the additional content directory. So far, this has been a reasonable compromise and I haven't seen any problems with performance or behaviour (I know others have with external storage). Sorry, it's not a perfect answer, but like a lot of the APIs added with iCloud I don't think Apple have finished documenting^H^H^H^H^H^H^H^H^H^H^H them.Lagrange
still can't understand that :(. How to save and to delete files from additional content. Is it possible to make some example?Wavemeter
If the additional content is an NSFileWrapper, shall I use the atomic option when writing? See this question.Hali
D
3

The documentation for -additionalContentForURL:error: indicates that returning a nil supposed to signal an error.

  A return value of nil indicates an error condition. To avoid generating 
  an exception, you must return a value from this method. If it is not always
  the case that there will be additional content, you should return a sentinel value (for example, an NSNull instance) that you check for in
  writeAdditionalContent:toURL:originalContentsURL:error:.

I override -writeContents:andAttributes:safelyToURL:forSaveOperation:error: for another purpose (doing some stuff on first save of a new document), and calling super invokes the NSException gods because contents value is nil, not an NSDictionary as seemingly expected by UIManagedDocument. Hmm.

The more you know...

P.S. I guess it depends on the time of day with -writeContents:andAttributes:... It once threw an exception complaining about expecting an NSDictionary, but later threw an exception complaining that I didn't pass it an NSData. My eyebrow could not be raised in a more Spock-like fashion than it is right now.

Deletion answered 14/2, 2012 at 0:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.