After countless attempts and sieving through every SO answer + google result, it baffles me that working with EXIF on iOS is so frustrating.
Below is working code with its results.
[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) { NSData *imageNSData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; CGImageSourceRef imgSource = CGImageSourceCreateWithData((__bridge_retained CFDataRef)imageNSData, NULL); //get all the metadata in the image NSDictionary *metadata = (__bridge NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imgSource, 0, NULL); NSLog(@"original metadata Info: %@",metadata); //make the metadata dictionary mutable so we can add properties to it NSMutableDictionary *metadataAsMutable = [metadata mutableCopy]; NSMutableDictionary *EXIFDictionary = [[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyExifDictionary]mutableCopy]; NSMutableDictionary *GPSDictionary = [[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyGPSDictionary]mutableCopy]; NSMutableDictionary *RAWDictionary = [[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyRawDictionary]mutableCopy]; if(!EXIFDictionary) EXIFDictionary = [[NSMutableDictionary dictionary] init]; if(!GPSDictionary) GPSDictionary = [[NSMutableDictionary dictionary] init]; if(!RAWDictionary) RAWDictionary = [[NSMutableDictionary dictionary] init]; [GPSDictionary setObject:@"camera coord Latitude" forKey:(NSString*)kCGImagePropertyGPSLatitude]; [GPSDictionary setObject:@"camera coord Longitude" forKey:(NSString*)kCGImagePropertyGPSLongitude]; [GPSDictionary setObject:@"camera GPS Date Stamp" forKey:(NSString*)kCGImagePropertyGPSDateStamp]; [GPSDictionary setObject:@"camera direction (heading) in degrees" forKey:(NSString*)kCGImagePropertyGPSImgDirection]; [GPSDictionary setObject:@"subject coord Latitude" forKey:(NSString*)kCGImagePropertyGPSDestLatitude]; [GPSDictionary setObject:@"subject coord Longitude" forKey:(NSString*)kCGImagePropertyGPSDestLongitude]; [EXIFDictionary setObject:@"[S.D.] kCGImagePropertyExifUserComment" forKey:(NSString *)kCGImagePropertyExifUserComment]; [EXIFDictionary setValue:@"69 m" forKey:(NSString *)kCGImagePropertyExifSubjectDistance]; //Add the modified Data back into the image’s metadata [metadataAsMutable setObject:EXIFDictionary forKey:(NSString *)kCGImagePropertyExifDictionary]; [metadataAsMutable setObject:GPSDictionary forKey:(NSString *)kCGImagePropertyGPSDictionary]; [metadataAsMutable setObject:RAWDictionary forKey:(NSString *)kCGImagePropertyRawDictionary]; NSLog(@"metadataAsMutable Info: %@",metadataAsMutable); CFStringRef UTI = CGImageSourceGetType(imgSource); //this is the type of image (e.g., public.jpeg) //this will be the data CGImageDestinationRef will write into NSMutableData *newImageData = [NSMutableData data]; CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)newImageData, UTI, 1, NULL); if(!destination) NSLog(@"***Could not create image destination ***"); //add the image contained in the image source to the destination, overidding the old metadata with our modified metadata CGImageDestinationAddImageFromSource(destination, imgSource, 0, (__bridge CFDictionaryRef) metadataAsMutable); //tell the destination to write the image data and metadata into our data object. //It will return false if something goes wrong BOOL success = NO; success = CGImageDestinationFinalize(destination); if(!success) NSLog(@"***Could not create data from image destination ***"); CIImage *testImage = [CIImage imageWithData:newImageData]; NSDictionary *propDict = [testImage properties]; NSLog(@"Properties %@", propDict); }]; Which outputs this:
2012-10-12 23:17:45.415 Waypointer[3120:907] original metadata Info: { ColorModel = RGB; DPIHeight = 72; DPIWidth = 72; Depth = 8; Orientation = 1; PixelHeight = 2448; PixelWidth = 3264; "{Exif}" = { ApertureValue = "2.526069"; BrightnessValue = "-4.410617"; ColorSpace = 1; ComponentsConfiguration = ( 1, 2, 3, 0 ); ExifVersion = ( 2, 2, 1 ); ExposureMode = 0; ExposureProgram = 2; ExposureTime = "0.06666667"; FNumber = "2.4"; Flash = 16; FlashPixVersion = ( 1, 0 ); FocalLenIn35mmFilm = 35; FocalLength = "4.28"; ISOSpeedRatings = ( 800 ); MeteringMode = 5; PixelXDimension = 3264; PixelYDimension = 2448; SceneCaptureType = 0; SensingMethod = 2; ShutterSpeedValue = "3.906905"; SubjectArea = ( 1631, 1223, 881, 881 ); WhiteBalance = 0; }; "{TIFF}" = { Orientation = 1; ResolutionUnit = 2; XResolution = 72; YResolution = 72; "_YCbCrPositioning" = 1; }; } And this:
2012-10-12 23:17:45.421 Waypointer[3120:907] metadataAsMutable Info: { ColorModel = RGB; DPIHeight = 72; DPIWidth = 72; Depth = 8; Orientation = 1; PixelHeight = 2448; PixelWidth = 3264; "{Exif}" = { ApertureValue = "2.526069"; BrightnessValue = "-4.410617"; ColorSpace = 1; ComponentsConfiguration = ( 1, 2, 3, 0 ); ExifVersion = ( 2, 2, 1 ); ExposureMode = 0; ExposureProgram = 2; ExposureTime = "0.06666667"; FNumber = "2.4"; Flash = 16; FlashPixVersion = ( 1, 0 ); FocalLenIn35mmFilm = 35; FocalLength = "4.28"; ISOSpeedRatings = ( 800 ); MeteringMode = 5; PixelXDimension = 3264; PixelYDimension = 2448; SceneCaptureType = 0; SensingMethod = 2; ShutterSpeedValue = "3.906905"; SubjectArea = ( 1631, 1223, 881, 881 ); SubjectDistance = "69 m"; UserComment = "[S.D.] kCGImagePropertyExifUserComment"; WhiteBalance = 0; }; "{GPS}" = { DateStamp = "camera GPS Date Stamp"; DestLatitude = "subject coord Latitude"; DestLongitude = "subject coord Longitude"; ImgDirection = "camera direction (heading) in degrees"; Latitude = "camera coord Latitude"; Longitude = "camera coord Longitude"; }; "{Raw}" = { }; "{TIFF}" = { Orientation = 1; ResolutionUnit = 2; XResolution = 72; YResolution = 72; "_YCbCrPositioning" = 1; }; } And, after it's all done, this:
2012-10-12 23:17:47.131 Waypointer[3120:907] Properties { ColorModel = RGB; DPIHeight = 72; DPIWidth = 72; Depth = 8; Orientation = 1; PixelHeight = 2448; PixelWidth = 3264; "{Exif}" = { ApertureValue = "2.526069"; BrightnessValue = "-4.410617"; ColorSpace = 1; ComponentsConfiguration = ( 0, 0, 0, 1 ); ExifVersion = ( 2, 2, 1 ); ExposureMode = 0; ExposureProgram = 2; ExposureTime = "0.06666667"; FNumber = "2.4"; Flash = 16; FlashPixVersion = ( 1, 0 ); FocalLenIn35mmFilm = 35; FocalLength = "4.28"; ISOSpeedRatings = ( 800 ); MeteringMode = 5; PixelXDimension = 3264; PixelYDimension = 2448; SceneCaptureType = 0; SensingMethod = 2; ShutterSpeedValue = "3.906905"; SubjectArea = ( 1631, 1223, 881, 881 ); UserComment = "[S.D.] kCGImagePropertyExifUserComment"; WhiteBalance = 0; }; "{JFIF}" = { DensityUnit = 1; JFIFVersion = ( 1, 1 ); XDensity = 72; YDensity = 72; }; "{TIFF}" = { Orientation = 1; ResolutionUnit = 2; XResolution = 72; YResolution = 72; "_YCbCrPositioning" = 1; }; } As the example illustrates, you can see the image's original metaData, its modification, and then its final output.
The final output is what bothers me because no matter what I do, I cannot get my modified values to stick!
Is there some very specific format I'm missing? Why is iOS stripping my modifications? What do I need to do to add these extra values? They are listed in the .header and figured it should be easily accepted.