Search code examples
iosswiftuidocumentnsfilewrapper

Handling image load/store in a UIDocument that contains multiple images


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


Solution

  • 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.