Jeshua Lacock
Jeshua Lacock

Reputation: 6678

Copying/moving multi-gigabyte images with NSFileCoordinator

My app is capable of writing multi-gigabyte images. I need a way to allow the user to do something with the full-resolution images (such as AirDrop). It does not appear possible to write the image to Photos or with UIKit since there may not be enough memory to read the whole image in RAM.

With UIActivityViewController I am able to copy the large images with a NSFileCoordinator, but only if I use an extension of .zip or .bin for the destination URL. The file is copied, but macOS thinks they are archives and not an image. After the file has been copied, changing the extension to .jpg on the Mac allows the copied image to be used, but that is less than an ideal user experience.

Zipping the image (that is already compressed) is not a viable solution since the whole image would need to be read to zip it, which may exceed the amount of memory available. Not to mention it is a total waste of time/power consumption.

How can I allow a user to copy multi-gigabyte images from my app, and ideally with UIActivityViewController?

Here is the code that allows the image to be copied. If I don't use .zip or .bin for the destination URL, the image isn't copied, nothing happens, no errors reported.

func copyImage() {
    
    let fm = FileManager.default
    var archiveUrl: URL?
    var error: NSError?
    let imageProductURL = TDTDeviceUtilites.getDocumentUrl(forFileName: "hugeImage.jpg")
    
    let coordinator = NSFileCoordinator()

    coordinator.coordinate(readingItemAt: imageProductURL, options: [.forUploading], error: &error) { (zipUrl) in
        let tmpUrl = try! fm.url(
            for: .itemReplacementDirectory,
            in: .userDomainMask,
            appropriateFor: zipUrl,
            create: true
        ).appendingPathComponent("hugeImage.jpg.zip")
        try! fm.moveItem(at: zipUrl, to: tmpUrl)
        archiveUrl = tmpUrl
    }

    if let archiveUrl = archiveUrl {
        let avc = UIActivityViewController(activityItems: [archiveUrl], applicationActivities: nil)
        present(avc, animated: true)
        
    } else {
        print(error)
    }
    
}

Upvotes: 1

Views: 146

Answers (0)

Related Questions