1

I have a simple model object Location with a few text items and a images : [UIImages]?. Location is Codable so I store the text bits as JSON and then write the images into the same FileWrapper.

My question is how to store the relationship between the image files and the [UIImage] array. The images have to come back in the same order. Is there a way I can hook into Coding so that the array gets replaced by the URLs pointing to the images?

Or alternately, should I always have the images as separate files, say in the cache directory, and replace the [UIImage] with [URL]s

Maury Markowitz
  • 9,082
  • 11
  • 46
  • 98
  • UIDocument does not require any "Coding" so it's unclear what the issue is. What format you save your document into and restore it from is totally up to you. Take a look at Apple's [sample code](https://developer.apple.com/library/content/samplecode/sc2281/Introduction/Intro.html) if you don't understand how to load images from a file wrapper into a UIDocument. – matt Feb 16 '18 at 18:48
  • @matt, this is a question about how to store the relationship between the images and the model object. Is that not clear? I can edit the question. – Maury Markowitz Feb 16 '18 at 18:58
  • No, it's clear. But it's totally up to you how to do it. You get to make up your own file format. You can't _not_ do it "correctly" because you get to define what "correctly" is. Again, I suggest you look at the sample code I linked to. You have not said how the images are to relate to the text in the display of your document, so all this is just hand-waving. – matt Feb 16 '18 at 18:59
  • @matt, it's not clear then. This has nothing to do with storing the files. It has to do with how you turn [UIImage] into something that can be stored in JSON. I will edit it to make this more clear. – Maury Markowitz Feb 16 '18 at 19:00
  • You _don't_ turn UIImage into something that can be stored in JSON. That's why you're using a file wrapper, it's so that you can store the UIImage in the file wrapper. I don't know why you're using JSON but if you're going to use it, the JSON you use is totally up to you. Look at the example I pointed you to! The document knows the _name_ of the image file. That's all you need. – matt Feb 16 '18 at 19:01
  • Yes, that's my point. I've re-written the question, hopefully it is more clear now. – Maury Markowitz Feb 16 '18 at 19:08
  • Yes but not "in the cache directory"; the images go in the file wrapper. You seem to say the _word_ file wrapper without grasping what a file wrapper _is_. Again, again, again I say: look at Apple's example. You can store the names of the image files as the array; that can be stored as text, JSON, whatever. The format of the file wrapper and how that relates to what you will assemble on loading is totally up to you. I think I've said that several times now. Would it help you if I wrote out the code for a file wrapper that loads several images in order? – matt Feb 16 '18 at 19:14
  • > store the names of the image files as the array I think this is the actual answer to the question I am asking. – Maury Markowitz Feb 16 '18 at 19:18
  • Okay, I put together some file wrapper code that you can actually run as a test. That will give you the idea of the sort of thing you need to be doing. – matt Feb 16 '18 at 20:03

1 Answers1

4

Here's an example of storing a bunch of image files in a file wrapper, along with an "index" (which I call list) of their names in a specific order:

    let fm = FileManager.default
    let docurl = fm.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let d = FileWrapper(directoryWithFileWrappers: [:])
    let imnames = ["manny.jpg", "moe.jpg", "jack.jpg"]
    for imname in imnames {
        let im = UIImage(named:imname)!
        let imfw = FileWrapper(regularFileWithContents: UIImageJPEGRepresentation(im, 1)!)
        imfw.preferredFilename = imname
        d.addFileWrapper(imfw)
    }
    let list = try! JSONEncoder().encode(imnames)
    let listfw = FileWrapper(regularFileWithContents: list)
    listfw.preferredFilename = "list"
    d.addFileWrapper(listfw)
    do {
        try d.write(to: docurl.appendingPathComponent("myFileWrapper"), originalContentsURL: nil)
        print("ok")
    } catch {
        print(error)
    }

And here's an example of reading that file wrapper by fetching the list and then getting the image files by name in that same order:

    let fm = FileManager.default
    let docurl = fm.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let fwurl = docurl.appendingPathComponent("myFileWrapper")
    do {
        let d = try FileWrapper(url: fwurl)
        if let list = d.fileWrappers?["list"]?.regularFileContents {
            let imnames = try! JSONDecoder().decode([String].self, from: list)
            for imname in imnames {
                if let imdata = d.fileWrappers?[imname]?.regularFileContents {
                    print("got image data for", imname)
                    // in real life, do something with the image here
                }
            }
        } else {
            print("no list")
        }
    } catch {
        print(error); return
    }

That prints:

got image data for manny.jpg
got image data for moe.jpg
got image data for jack.jpg

All you want to do in UIDocument is that same thing, except that UIDocument will write and read the file wrapper for you.

matt
  • 515,959
  • 87
  • 875
  • 1,141