That shouldn't have been an answer, missed the comment button.
Anyway, I have replicated the issue in a single test. For me the issue manifests itself in iOS 15, if I run the test on a device running ios 14, the test passes.
Here is the full code for a test, you'll need to supply your own image and change a tag that exists for you:
func testCGImageDestinationCopyImageSource() throws {
guard let imageURL = Bundle(for: self.classForCoder).url(forResource: "Image_000001", withExtension: "jpg") else {
XCTFail()
return
}
// Work with the image data
let originalData = try Data(contentsOf: imageURL)
// Create source from data
guard let imageSource = CGImageSourceCreateWithData(originalData as CFData, nil) else {
XCTFail()
return
}
guard let UTI: CFString = CGImageSourceGetType(imageSource) else {
XCTFail()
return
}
// Setup a new destination to copy data too
let imageData: CFMutableData = CFDataCreateMutable(nil, 0)
guard let destination = CGImageDestinationCreateWithData(imageData as CFMutableData, UTI, 1, nil) else {
XCTFail()
return
}
// Get the metadata
var mutableMetadata: CGMutableImageMetadata
if let imageMetadata = CGImageSourceCopyMetadataAtIndex(imageSource, 0, nil) {
mutableMetadata = CGImageMetadataCreateMutableCopy(imageMetadata) ?? CGImageMetadataCreateMutable()
} else {
mutableMetadata = CGImageMetadataCreateMutable()
}
// Inspect and check the old value
guard let tag = CGImageMetadataCopyTagMatchingImageProperty(mutableMetadata,
kCGImagePropertyExifDictionary,
kCGImagePropertyExifLensModel) else {
XCTFail()
return
}
guard let originalValue = CGImageMetadataTagCopyValue(tag) as? String else {
XCTFail()
return
}
XCTAssertEqual(originalValue, "iOS.0")
// Set a new value in the metadata
CGImageMetadataSetValueMatchingImageProperty(mutableMetadata,
kCGImagePropertyExifDictionary,
kCGImagePropertyExifLensModel, "iOS" as CFString)
// Ensure new value is set in the metadata
guard let newTag = CGImageMetadataCopyTagMatchingImageProperty(mutableMetadata,
kCGImagePropertyExifDictionary,
kCGImagePropertyExifLensModel) else {
XCTFail()
return
}
guard let newValue = CGImageMetadataTagCopyValue(newTag) as? String else {
XCTFail()
return
}
XCTAssertEqual(newValue, "iOS")
// Combine the new metadata with the original image
let options = [
kCGImageDestinationMetadata as String : mutableMetadata,
kCGImageDestinationMergeMetadata as String : true
] as [String : Any]
guard CGImageDestinationCopyImageSource(destination, imageSource, options as CFDictionary, nil) else {
XCTFail()
return
}
// Create a new source from the copied to data
guard let newSource = CGImageSourceCreateWithData(imageData as CFData, nil) else {
XCTFail()
return
}
// Get the metadata from the copied to data
var mutableMetadata2: CGMutableImageMetadata
if let imageMetadata2 = CGImageSourceCopyMetadataAtIndex(newSource, 0, nil) {
mutableMetadata2 = CGImageMetadataCreateMutableCopy(imageMetadata2) ?? CGImageMetadataCreateMutable()
} else {
mutableMetadata2 = CGImageMetadataCreateMutable()
}
// Inspect and check the value in the copied to data
guard let updatedTag = CGImageMetadataCopyTagMatchingImageProperty(mutableMetadata2,
kCGImagePropertyExifDictionary,
kCGImagePropertyExifLensModel) else {
XCTFail()
return
}
guard let updatedValue = CGImageMetadataTagCopyValue(updatedTag) as? String else {
XCTFail()
return
}
XCTAssertEqual(updatedValue, "iOS")
}