iosswiftuiimagensdataheic

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


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.


Solution

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