4

I am trying to store a plist and several binary files (let's say images) as part of an UIManagedDocument. The name of the binary files are an attribute in Core Data and I don't need to enumerate them, just access the right one when showing the related entity.

The file structure that I want to have is:

 - <File yyyyMMdd-HHmmss>.extdoc
   - StoreContent
     - persistentStore
   - AdditionalContent
     - ListStatus.plist (used to store per document defaults)
       - Images
         - uuid1.png
         - uuid2.png
         - ...
         - uuidn.png

So far, I have successfully followed the instructions in How do I save additional content into my UIManagedDocument file packages?, but when I try to add the binary files there are some things that I don't know how to do.

  1. Should I treat the URL /the/path/File yyyyMMdd-HHmmss.extdoc/AdditionalContent (the default one provided with readAdditionalContentFromURL:error:) as a NSFileWrapper? Are there any advantages/disadvantages vs just using the URLs? I find it more complicated to use the file wrapper, since the plist has to be read using the file wrapper accessors and NSCoder (I guess), and the files, I have to store the file wrapper for the Images directory and then obtain the corresponding node with objectForKey (I assume). But Apple's Document-Based Apps Programming Guide for iOS regarding custom formats instead of NSData or NSFileWrapper, states "Keep in mind that your code will have to duplicate what UIDocument does for you, and so you must deal with greater complexity and a greater possibility of error." Am I misunderstanding this?
  2. Per document defaults are declared as properties: the setter modifies the NSDictionary that maps the plist and marks the document as updated, and the getter accesses the dictionary with the proper key. How do I expose the ability to read/write the binary files? Should I add a method to my subclass of UIManagedDocument? - (void)writeImage:(NSString*)uuid; and -(UIImage *)readImage:(NSString *)uuid; And should I keep this data in memory until the document is saved? How?
  3. Assuming that NSFileWrapper is the way to go, if I plan to use this document with iCloud should I use file coordinators with the file wrapper? If so, how?

Any source code for each question will be greatly appreciated. Thank you.

P.S.: I know that I could save some binary data inside of Core Data, but I don't feel comfortable with that solution. Among other reasons, I rather store the PNG data for image files that a serialized version of UIImage that won't be compatible with NSImage if I want to create a desktop app.

Community
  • 1
  • 1
Jorge Ortiz
  • 1,556
  • 1
  • 16
  • 19

3 Answers3

1

I'd like to say that, in general I rather like UIManagedDocument. It has a few advantages over raw Core Data. For example, it sets up the entire core data stack for you automatically. It also sets up nested managed object contexts for you, so you get free background saving. None of that is particularly earth-shattering, but it's a lot of functionality from a tiny amount of code.

I haven't played around with saving additional information...but here are my thoughts.

First, you shouldn't need to treat the new URL as a file wrapper. You should just be able to do regular file operations on the provided URL. Just make sure you have everything implemented properly in additionalContentForURL:error:, writeAdditionalContent:toURL:originalContentsURL:error: and readAdditionalContentFromURL:error:. The read and write operations need to be symmetric. And you should probably snapshot your data in additionalContentsForURL:error: so that everything will be saved in a known, good state (since the save operations are asynchronous).

As an alternative, have you considered using the Store in External Record File flag in your data model instead of saving it manually? This should force Core Data to (depending on the size of the binary data) automatically store them externally. I looked at the release notes, and I didn't see anything saying you couldn't use this feature with iCloud. That might be the easiest fix.

0

Attacking a side point for the moment (as I have not had ANY good experience with UIManagedDocument).

You can save the binary inside of Core Data for a iOS 5.0+ application using the external file reference. Then you can save the PNG of the image to Core Data directly and not need to worry about a UIManagedDocument or about bloating the sqlite file.

There is nothing stopping you from storing the PNG instead of a UIImage.

Marcus S. Zarra
  • 46,571
  • 9
  • 101
  • 182
  • Thanks. Would that put the contents inside of the PNG inside of the UIManagedDocument? – Jorge Ortiz Feb 29 '12 at 14:33
  • If you used a `UIManagedDocument` then yes. However, you could also try doing it *without* a `UIManagedDocument` and see what happens. I am still quite dubious of the need for `UIManagedDocument` for all but the most unusual cases. – Marcus S. Zarra Feb 29 '12 at 17:48
  • Being able to use iCloud to share the document(s) with other users that don't use the same iCloud account is my motivation. – Jorge Ortiz Mar 01 '12 at 11:05
  • I am curious how you are going to do that. Using a straight Core Data implementation you can sync with iCloud so I am curious what difference you are utilizing that is in UIManagedDocument. – Marcus S. Zarra Mar 01 '12 at 17:31
  • In order to share a document with another user, and let the other user have both documents, the Core Data + iCloud solution does not work. The plan is to share the url via URLForPublishingUbiquitousItemAtURL:expirationDate:error:, but that requires encapsulation in a UIDocument (subclass) wrapper. – Jorge Ortiz Mar 02 '12 at 09:37
  • Ahh, that is an interesting idea. Thank you for sharing. – Marcus S. Zarra Mar 02 '12 at 16:14
  • @MarcusS.Zarra Are you still having bad UIManagedDocument experiences? I had initial bad experiences, but after I "got it" it seems really nice. I have deep respect for you (I have your books and iDeveloper.tv videos), so after reading that, I wonder what I am missing. – Jody Hagins Apr 17 '12 at 22:29
  • Thank you :) My opinion of `UIManagedDocument` has not changed although I suspect a large portion of that rests on the shoulders of iCloud. `UIManagedDocument` abstracts a lot away and makes things to opaque for my tastes. iCloud is still definitely "rough". – Marcus S. Zarra Apr 18 '12 at 14:35
0

One other thought. You may need to use an NSFileCoordinator for the read and write operations. Technically, any read or write operations in the iCloud container need to use a file coordinator (to coordinate with the iCloud sync service--this prevents accidentally corrupting a file by reading it while another process is writing to it).

I know that UIDocument wraps most of its input and output methods automatically. I'd guess that these methods are similarly wrapped (since they give you a URL to use)--However, the docs aren't very clear.

  • After re-reading the docs again UIDocument does "Coordinated reading and writing of document files that is automatically integrated with cloud services." But for writeAdditionalContent:toURL:originalContentsURL:error: that is invoked as part of writeContents:andAttributes:safelyToURL:forSaveOperation:error: it states that "reading and writing of document data [...] must be coordinated with other attempts to read from and write to the same document file." As you said, not clear at all. Is there any way to test if it is required? – Jorge Ortiz Mar 12 '12 at 00:10