Kvazios
Kvazios

Reputation: 126

Decoding Base64 image in swift

I need to convert the image to/from Base64. All working fine for JPG files, but if I upload PNG and then open it in the app it leads to the crash with error

"Unexpectedly found nil while unwrapping an Optional value"

while trying to create Data from the encoded string

Here is my code:

For Encoding:

static  func base64Convert(base64String: String?) -> UIImage {
    var decodedImage = #imageLiteral(resourceName: "no_prof_image")
    if ((base64String?.isEmpty)! || (base64String?.contains("null"))!) {
        return decodedImage
    }else {
        if  let imageBase64String = base64String,
            let dataDecoded = Data(base64Encoded: imageBase64String, options: .ignoreUnknownCharacters) {
            decodedImage = UIImage(data: dataDecoded) ?? #imageLiteral(resourceName: "no_prof_image")
        }
        return decodedImage
    }
}

For Decoding:

static func makeProfileBase64FromImage(image: UIImage) -> String? {
    var imageData : Data?
    if let jpegData = UIImageJPEGRepresentation(image, 1.0) {
        imageData = jpegData
    } else if let pngData = UIImagePNGRepresentation(image) {
        imageData = pngData
    }
    return imageData?.base64EncodedString()
}

What I tried:

1) All encoding options

2) All decoding options

3) Swap UIImageJPEGRepresentation to UIImagePNGRepresentation. It leads to the same error but with jpg images.

UPDATE

Here is code how I send data to the server:

var imageToSend : String = "null"

        if profileImage.image != #imageLiteral(resourceName: "no_prof_image"),
            let validImage = profileImage.image,
            let imageBase64 = AppUtils.makeProfileBase64FromImage(image: validImage) {
            imageToSend = imageBase64
        }

let parameters : Parameters = [
    "image": imageToSend
]

Alamofire.request(AppUtils.API_URL + "update_profile.php", method: .post, parameters: parameters)
    .validate().responseData() {response in

    switch response.result {
        case .success:
            //...Some stuff
            break
        case .failure:
            //...Some stuff
            break
    }
}

Part of the string that came to the server:

/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAABkKADAAQAAAABAAABkAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgBkAGQAwERAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//E

UPDATED CODE IN THE QUESTION

For now, the code doesn't have force unwrap. But now I always gets standard #imageLiteral(resourceName: "no_prof_image"). (Before, at least jpg works fine :) )

Upvotes: 1

Views: 1927

Answers (3)

Leo Dabus
Leo Dabus

Reputation: 236315

Looks like your issue is your PNG data size which is much bigger than JPEG data. So your server might have a size limit for your image upload.

Regarding your encoding method The second condition else if let pngData = UIImagePNGRepresentation(image) will never be executed. You have to choose which one you would like to use PNG or JPEG data representations (JPEG most times due to the size limit). Btw this would be much easier using optional chaining.

return UIImageJPEGRepresentation(image, 1)?.base64EncodedString()

Swift 4.2 Xcode 10 or later

return image.jpegData(compressionQuality: 1)?.base64EncodedString()

Upvotes: 1

ekscrypto
ekscrypto

Reputation: 3806

As @mag_zbc suggested, start with:

static func makeBase64FromImage(image: UIImage) -> String? {
    var imageData : Data?
    if let jpegData = UIImageJPEGRepresentation(image, 1.0) {
        imageData = jpegData
    } else if let pngData = UIImagePNGRepresentation(image) {
        imageData = pngData
    }
    return imageData?.base64EncodedString()
}

Then, update this code to:

var imageToSend : String = "null"
if profileImage.image != #imageLiteral(resourceName: "no_prof_image"),
   let validImage = profileImage.image,
   let imageBase64 = AppUtils.makeBase64FromImage(image: validImage) {
       imageToSend = imageBase64
}
let parameters : Parameters = [
    "image": imageToSend
]
...

In general, you want to avoid using "!" anywhere unless you can 100% confirm that in any and all cases the value will always be defined. In this case, I believe the issue was your code being called with profileImage.image == nil

A profileImage.image being nil would != to the image literal, and therefore would have entered the conditional if you defined. Then by forcing it to be unwrapped with "!" you tried to unwrap nil.

Good luck!

Upvotes: 1

mag_zbc
mag_zbc

Reputation: 6982

Quite obviously, you use UIImageJPEGRepresentation for .jpeg images, but for .png images you should use UIImagePNGRepresentation

Also, don't use force unwrapping.

static func makeBase64FromImage(image: UIImage) -> String? {
    var imageData : Data?
    if let jpegData = UIImageJPEGRepresentation(image, 1.0) {
        imageData = jpegData
    } else if let pngData = UIImagePNGRepresentation(image) {
        imageData = pngData
    }
    return imageData?.base64EncodedString()
}

Upvotes: 3

Related Questions