How can I convert from UIImage to HEIF / HEIC Data in Swift?
Asked Answered
C

2

10

Is NSKeyedArchiver appropriate to convert UIImage to Data?

do {
    let data = try NSKeyedArchiver.archivedData(withRootObject: UIImage(named: somePath), requiringSecureCoding: true)
    ...
} catch {
    print(error)
}

Or is it overkill and using pngData() is more appropriate?

let image = UIImage(named: somePath)
let data = image?.pngData()

and how can I convert from UIImage to HEIF / HEIC Data ?

The goal is to save the image to the device's file system.

Coaptation answered 16/5, 2020 at 0:5 Comment(0)
I
29

No. Never use NSKeyedArchiver to convert your image to Data. Choose an image format (HEIC, PNG, JPEG, etc) and get its data representation. You should only use PNG when saving images to use in your UI. Most of the time jpeg is the preferred choice. If the device supports HEIC it is an option considering the image quality and reduced data size.

If you need to check if the user device supports HEIC type you can do it as follow:

var isHeicSupported: Bool {
    (CGImageDestinationCopyTypeIdentifiers() as! [String]).contains("public.heic")
}

If you need to convert your image to HEIC you need to get a CGImage from your UIImage and convert your UIImage's imageOrientation to CGImagePropertyOrientation to preserve the orientation when creating its data representation:

extension UIImage {
    var heic: Data? { heic() }
    func heic(compressionQuality: CGFloat = 1) -> Data? {
        guard
            let mutableData = CFDataCreateMutable(nil, 0),
            let destination = CGImageDestinationCreateWithData(mutableData, "public.heic" as CFString, 1, nil),
            let cgImage = cgImage 
        else { return nil }
        CGImageDestinationAddImage(destination, cgImage, [kCGImageDestinationLossyCompressionQuality: compressionQuality, kCGImagePropertyOrientation: cgImageOrientation.rawValue] as CFDictionary)
        guard CGImageDestinationFinalize(destination) else { return nil }
        return mutableData as Data
    }
}

extension CGImagePropertyOrientation {
    init(_ uiOrientation: UIImage.Orientation) {
        switch uiOrientation {
            case .up: self = .up
            case .upMirrored: self = .upMirrored
            case .down: self = .down
            case .downMirrored: self = .downMirrored
            case .left: self = .left
            case .leftMirrored: self = .leftMirrored
            case .right: self = .right
            case .rightMirrored: self = .rightMirrored
        @unknown default:
            fatalError()
        }
    }
}

extension UIImage {
    var cgImageOrientation: CGImagePropertyOrientation { .init(imageOrientation) }
}

Usage for lossless compression:

if isHeicSupported, let heicData = image.heic {
    // write your heic image data to disk
}

or adding compression to your image:

if isHeicSupported, let heicData = image.heic(compressionQuality: 0.75) {
    // write your compressed heic image data to disk
}
Ic answered 16/5, 2020 at 1:30 Comment(10)
If I wanted to include some metadata with each image that I write to disk, such as a timestamp, and I either subclassed UIImage with these meta properties or wrapped the image in a struct with the properties, what would the recommended data serialization be?Coaptation
Why do you need the timestamp? You can use the url resource creation dateIc
If you need a name for the image you can do the same as Apple IMG_0000.heic or something similarIc
You can easily sort your images and generate the next number or generate an integer sequentially and store the last number in UserDefaultsIc
I don’t think subclassing UIImage a good idea. It would be easier to create your own struct and add a fileURL property to associate an image to itIc
I want to cache these images on disk and constantly record their last use so when it comes time to delete old images, I can sort them by this value. Do you think wrapping them in a struct is the way to go? If so, then must I use NSKeyedArchiver?Coaptation
You can just fetch your image directory contents and sort by date creationIc
Why would you convert your data to a property list? I suggest you use json file (Codable Protocol) if you need to store anything on disk or send it to your server.Ic
Every time the image is read from cache, I want to update the timestamp. This way, when it comes time to clean up the cache, I delete the least-recently-used images first.Coaptation
developer.apple.com/documentation/foundation/urlresourcekey/… extension URL { var contentAccessDate: Date? { (try? resourceValues(forKeys: [.contentAccessDateKey]))?.contentAccessDate } }Ic
T
1

This is now supported in iOS 17.

let image = UIImage(named: somePath)
let data = image?.heicData()

https://developer.apple.com/documentation/uikit/uiimage/4210341-heicdata

Thanet answered 29/5, 2024 at 2:32 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.