Converting an Image from Heic to Jpeg/Jpg

Viewed 19171

I have an application where user can upload multiple images and all the images will be stored in a server and will be displayed on a web view in my iOS application.

Now everything used to work just about fine till iOS 10 but suddenly we started seeing some pictures/ images not being displayed , after a little debugging we found out that this is the problem caused because of the new image format of apple (HEIC),

I tried changing back to the Native UIImagePicker (picks only one image) and the images are being displayed as Apple I guess is converting the Image from HEIC to JPG when a user picks them, but this is not the case when I use 3rd party libraries as I need to implement multiple image picker.

Though we are hard at work to make the conversion process on the server side to avoid users who have not updated the app to face troubles, I also want to see if there is any way in which I can convert the image format locally in my application.

4 Answers

There's a workaround to convert HEIC photos to JPEG before uploading them to the server :

NSData *jpgImageData = UIImageJPEGRepresentation(image, 0.7);

If you use PHAsset, the, in order to have the image object, you'll need to call this method from PHImageManager:

- (PHImageRequestID)requestImageForAsset:(PHAsset *)asset targetSize:(CGSize)targetSize contentMode:(PHImageContentMode)contentMode options:(nullable PHImageRequestOptions *)options resultHandler:(void (^)(UIImage *__nullable result, NSDictionary *__nullable info))resultHandler;

On server side you also have the ability to use this API or this website directly

I've done it this way,

     let newImageSize = Utility.getJpegData(imageData: imageData!, referenceUrl: referenceUrl!)

     /**
         - Convert heic image to jpeg format
     */
     public static func getJpegData(imageData: Data, referenceUrl: NSURL) -> Data {
         var newImageSize: Data?
         if (try? Data(contentsOf: referenceUrl as URL)) != nil
         {
                let image: UIImage = UIImage(data: imageData)!
                newImageSize = image.jpegData(compressionQuality: 1.0)
         }
            return newImageSize!
     }

In Swift 3, given an input path of an existing HEIF pic and an output path where to save the future JPG file:

func fromHeicToJpg(heicPath: String, jpgPath: String) -> UIImage? {
        let heicImage = UIImage(named:heicPath)
        let jpgImageData = UIImageJPEGRepresentation(heicImage!, 1.0)
        FileManager.default.createFile(atPath: jpgPath, contents: jpgImageData, attributes: nil)
        let jpgImage = UIImage(named: jpgPath)
        return jpgImage
    }

It returns the UIImage of the jpgPath or null if something went wrong.

I have found the existing answers to be helpful but I have decided to post my take on the solution to this problem as well. Hopefully it's a bit clearer and "complete".

This solution saves the image to a file.

private let fileManager: FileManager

func save(asset: PHAsset, to destination: URL) {
    let options = PHContentEditingInputRequestOptions()
    options.isNetworkAccessAllowed = true

    asset.requestContentEditingInput(with: options) { input, info in
        guard let input = input, let url = input.fullSizeImageURL else {
            return // you might want to handle this case
        }

        do {
            try self.save(input, at: url, to: destination)
            // success!
        } catch {
            // failure, handle the error!
        }
    }
}

private func copy(
    _ input: PHContentEditingInput, at url: URL, to destination: URL
) throws {
    let uniformType = input.uniformTypeIdentifier ?? ""
    switch uniformType {
    case UTType.jpeg.identifier:
        // Copy JPEG files directly
        try fileManager.copyItem(at: url, to: destination)
    default:
        // Convert HEIC/PNG and other formats to JPEG and save to file
        let image = UIImage(data: try Data(contentsOf: url))
        guard let data = image?.jpegData(compressionQuality: 1) else {
            return // you might want to handle this case
        }
        try data.write(to: destination)
    }
}
Related