3

I'm trying to get rid of the EXIF data from a picture taken with AVFoundation, How can I do this in swift (2) preferred, Objective-C is okay too, I know how to convert the code to swift.

Why? I have done my research and I see a lot of famous Social Media (Reddit Source and many more) do remove EXIF data for identity purposes and other purposes.

If you think this is duplicate post, please read what I'm asking and provide link. Thank you.

Brian Nezhad
  • 6,148
  • 9
  • 44
  • 69

4 Answers4

14

My answer is based a lot on this previous question. I adapted the code to work on Swift 2.0.

class ImageHelper {
  static func removeExifData(data: NSData) -> NSData? {
    guard let source = CGImageSourceCreateWithData(data, nil) else {
        return nil
    }
    guard let type = CGImageSourceGetType(source) else {
        return nil
    }
    let count = CGImageSourceGetCount(source)
    let mutableData = NSMutableData(data: data)
    guard let destination = CGImageDestinationCreateWithData(mutableData, type, count, nil) else {
        return nil
    }
    // Check the keys for what you need to remove
    // As per documentation, if you need a key removed, assign it kCFNull
    let removeExifProperties: CFDictionary = [String(kCGImagePropertyExifDictionary) : kCFNull, String(kCGImagePropertyOrientation): kCFNull]

    for i in 0..<count {
        CGImageDestinationAddImageFromSource(destination, source, i, removeExifProperties)
    }

    guard CGImageDestinationFinalize(destination) else {
        return nil
    }

    return mutableData;
  }
}

Then you can simply do something like this:

let imageData = ImageHelper.removeExifData(UIImagePNGRepresentation(image))

In my example, I remove the rotation and the EXIF data. You can easily search the keys if you need anything else removed. Just make extra checks on the data generated as it is an optional.

Community
  • 1
  • 1
Gabriel Cartier
  • 1,734
  • 20
  • 22
  • 2
    you will need import ImageIO in your ImageHelper class – Xitcod13 Aug 04 '16 at 07:08
  • Ported this code to Swift 3 and setting kcfNull does not remove a property, but e.g. I can change it to something else by setting something like `kCGImagePropertyOrientation as String: 1` and the result will have the orientation value of 3. But I want to remove any exif data, any ideas how to do it since kcfNull is not working? – Milos Zikic Jan 11 '17 at 22:35
  • @MilosZikic have you checked the documentation? Haven't worked on that in a while, but I know it was written in the documentation how to set or remove values. – Gabriel Cartier Feb 07 '17 at 19:24
  • @GabrielCartier, It removes the META data, but JPEG image is getting converted to PNG. and image size is getting increased. Any suggestion on it..? – Gokul G Aug 02 '19 at 11:56
  • @GokulG @Gabriel Cartier: I tried the above solution but kCFNull is not removing anything. trying the below code. `let removeExifProperties: CFDictionary = [String(kCGImagePropertyExifDictionary) : kCFNull, String(kCGImagePropertyOrientation): kCFNull, String(kCGImagePropertyTIFFDictionary):kCFNull, String(kCGImagePropertyJFIFDictionary):kCFNull, String(kCGImagePropertyDepth):kCFNull] as CFDictionary` how did you remove this data. any help ? – Zac24 Feb 12 '21 at 14:44
2

Swift 5 version of the accepted answer:

extension Data {
    func byRemovingEXIF() -> Data? {
        guard let source = CGImageSourceCreateWithData(self as NSData, nil),
              let type = CGImageSourceGetType(source) else
        {
            return nil
        }

        let count = CGImageSourceGetCount(source)
        let mutableData = NSMutableData()
        guard let destination = CGImageDestinationCreateWithData(mutableData, type, count, nil) else {
            return nil
        }

        let exifToRemove: CFDictionary = [
            kCGImagePropertyExifDictionary: kCFNull,
            kCGImagePropertyGPSDictionary: kCFNull,
        ] as CFDictionary

        for index in 0 ..< count {
            CGImageDestinationAddImageFromSource(destination, source, index, exifToRemove)
            if !CGImageDestinationFinalize(destination) {
                print("Failed to finalize")
            }
        }

        return mutableData as Data
    }
}
rebello95
  • 8,486
  • 5
  • 44
  • 65
2

Here is a solution that removes the exif from raw data. Exif in jpeg is inside APP1 frame. Frame start is indicated with FF_E1. Frame end at next FF byte that does not follow 00 value.

var data: Data = ... // read jpeg one way or another 
var app1_start = 0
var app1_end = Int.max
for i in 0 ..< data.count {
    if data[i] == 0xFF {
        if data[i + 1] == 0xE1 {
            print("found start \(i)")
            app1_start = i
        } else if app1_start > 0, data [i] != 0x00 {
            app1_end = i - 1
            print("found end \(i-1)")
            break
        }
    }
}
data.removeSubrange(Range(app1_start...app1_end))

Data in this example is assumed to be jpeg. Code loops through byte array and stores APP1 start and end. Then removes the data from original mutable data. More about jpeg structure here https://en.wikipedia.org/wiki/JPEG

JariK
  • 31
  • 1
  • 3
1

You have UIImage right? Then you can convert UIImage to Data and save it to image again new image will not have any EXIF data

Swift 3

let imageData:Data = UIImagePNGRepresentation(image!)!



func saveToPhotoLibrary_iOS9(data:NSData, completionHandler: @escaping (PHAsset?)->()) {
    var assetIdentifier: String?
    PHPhotoLibrary.requestAuthorization { (status:PHAuthorizationStatus) in
        if(status == PHAuthorizationStatus.authorized){

            PHPhotoLibrary.shared().performChanges({
                let creationRequest = PHAssetCreationRequest.forAsset()
                let placeholder = creationRequest.placeholderForCreatedAsset

                creationRequest.addResource(with: PHAssetResourceType.photo, data: data as Data, options: nil)
                assetIdentifier = placeholder?.localIdentifier

            }, completionHandler: { (success, error) in
                if let error = error {
                    print("There was an error saving to the photo library: \(error)")
                }

                var asset: PHAsset? = nil
                if let assetIdentifier = assetIdentifier{
                    asset = PHAsset.fetchAssets(withLocalIdentifiers: [assetIdentifier], options: nil).firstObject//fetchAssetsWithLocalIdentifiers([assetIdentifier], options: nil).firstObject as? PHAsset
                }
                completionHandler(asset)
            })
        }else {
            print("Need authorisation to write to the photo library")
            completionHandler(nil)
        }
    }

}