Created
June 8, 2022 16:10
-
-
Save lukebrandonfarrell/961a6dbc8367f0ac9cabc89b0052d1fe to your computer and use it in GitHub Desktop.
A Swift class for extracting exif data from URL, UIImage or Data types ðŸ”
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ExifData.swift | |
// Qeepsake | |
// | |
// Created by Luke Farrell on 26/05/2022. | |
// | |
import Foundation | |
import ImageIO | |
class ExifData: NSObject { | |
var colorModel: String?; | |
var pixelWidth: Double?; | |
var pixelHeight: Double?; | |
var dpiWidth: Int?; | |
var dpiHeight: Int?; | |
var depth: Int?; | |
var orientation: Int?; | |
var apertureValue: String?; | |
var brightnessValue: String?; | |
var dateTimeDigitized: String?; | |
var dateTimeOriginal: String?; | |
var offsetTime: String?; | |
var offsetTimeDigitized: String?; | |
var offsetTimeOriginal: String?; | |
var model: String?; | |
var software: String?; | |
var tileLength: Double?; | |
var tileWidth: Double?; | |
var xResolution: Double?; | |
var yResolution: Double?; | |
var altitude: String?; | |
var destBearing: String?; | |
var hPositioningError: String?; | |
var imgDirection: String?; | |
var latitude: String?; | |
var longitude: String?; | |
var speed: Double?; | |
private var dictionary: [String: Any] { | |
return [ | |
"colorModel": colorModel as Any, | |
"pixelWidth": pixelWidth as Any, | |
"pixelHeight": pixelHeight as Any, | |
"dpiWidth": dpiWidth as Any, | |
"dpiHeight": dpiHeight as Any, | |
"depth": depth as Any, | |
"orientation": orientation as Any, | |
"apertureValue": apertureValue as Any, | |
"brightnessValue": brightnessValue as Any, | |
"dateTimeDigitized": dateTimeDigitized as Any, | |
"dateTimeOriginal": dateTimeOriginal as Any, | |
"offsetTime": offsetTime as Any, | |
"offsetTimeDigitized": offsetTimeDigitized as Any, | |
"offsetTimeOriginal": offsetTimeOriginal as Any, | |
"model": model as Any, | |
"software": software as Any, | |
"tileLength": tileLength as Any, | |
"tileWidth": tileWidth as Any, | |
"xResolution": xResolution as Any, | |
"yResolution": yResolution as Any, | |
"altitude": altitude as Any, | |
"destBearing": destBearing as Any, | |
"hPositioningError": hPositioningError as Any, | |
"imgDirection": imgDirection as Any, | |
"latitude": latitude as Any, | |
"longitude": longitude as Any, | |
"speed": speed as Any | |
] | |
} | |
public var toDictionary: NSDictionary { | |
return dictionary as NSDictionary; | |
} | |
init(data: Data) { | |
super.init(); | |
self.setExifData(data: data as CFData); | |
} | |
init(url: URL) { | |
super.init(); | |
if let data = NSData(contentsOf: url) { | |
self.setExifData(data: data); | |
} | |
} | |
init(image: UIImage) { | |
super.init(); | |
if let data = image.cgImage?.dataProvider?.data { | |
self.setExifData(data: data); | |
} | |
} | |
func setExifData(data: CFData) { | |
let options = [kCGImageSourceShouldCache as String: kCFBooleanFalse]; | |
if let imgSrc = CGImageSourceCreateWithData(data, options as CFDictionary) { | |
if let metadata = CGImageSourceCopyPropertiesAtIndex(imgSrc, 0, options as CFDictionary) as? NSDictionary { | |
self.colorModel = metadata[kCGImagePropertyColorModel] as? String; | |
self.pixelWidth = metadata[kCGImagePropertyPixelWidth] as? Double; | |
self.pixelHeight = metadata[kCGImagePropertyPixelHeight] as? Double; | |
self.dpiWidth = metadata[kCGImagePropertyDPIWidth] as? Int; | |
self.dpiHeight = metadata[kCGImagePropertyDPIHeight] as? Int; | |
self.depth = metadata[kCGImagePropertyDepth] as? Int; | |
self.orientation = metadata[kCGImagePropertyOrientation] as? Int; | |
if let tiffData = metadata[kCGImagePropertyTIFFDictionary] as? NSDictionary { | |
self.model = tiffData[kCGImagePropertyTIFFModel] as? String; | |
self.software = tiffData[kCGImagePropertyTIFFSoftware] as? String; | |
self.tileLength = tiffData[kCGImagePropertyTIFFTileLength] as? Double; | |
self.tileWidth = tiffData[kCGImagePropertyTIFFTileWidth] as? Double; | |
self.xResolution = tiffData[kCGImagePropertyTIFFXResolution] as? Double; | |
self.yResolution = tiffData[kCGImagePropertyTIFFYResolution] as? Double; | |
} | |
if let exifData = metadata[kCGImagePropertyExifDictionary] as? NSDictionary { | |
self.apertureValue = exifData[kCGImagePropertyExifApertureValue] as? String; | |
self.brightnessValue = exifData[kCGImagePropertyExifBrightnessValue] as? String; | |
self.dateTimeDigitized = exifData[kCGImagePropertyExifDateTimeDigitized] as? String; | |
self.dateTimeOriginal = exifData[kCGImagePropertyExifDateTimeOriginal] as? String; | |
if #available(iOS 13.0, *) { | |
self.offsetTime = exifData[kCGImagePropertyExifOffsetTime] as? String | |
self.offsetTimeDigitized = exifData[kCGImagePropertyExifOffsetTimeDigitized] as? String; | |
self.offsetTimeOriginal = exifData[kCGImagePropertyExifOffsetTimeOriginal] as? String; | |
} else { | |
// Fallback on earlier versions | |
}; | |
} | |
if let gpsData = metadata[kCGImagePropertyGPSDictionary] as? NSDictionary { | |
self.altitude = gpsData[kCGImagePropertyGPSAltitude] as? String; | |
self.destBearing = gpsData[kCGImagePropertyGPSDestBearing] as? String; | |
self.hPositioningError = gpsData[kCGImagePropertyGPSHPositioningError] as? String; | |
self.imgDirection = gpsData[kCGImagePropertyGPSImgDirection] as? String; | |
self.latitude = gpsData[kCGImagePropertyGPSLatitude] as? String; | |
self.longitude = gpsData[kCGImagePropertyGPSLongitude] as? String; | |
self.speed = gpsData[kCGImagePropertyGPSSpeed] as? Double; | |
} | |
} | |
} | |
} | |
} |
@nidegen Thanks! :) my swift is pretty rusty, what are the advantages to using a struct
?
I see. No big advantages. Classes are reference types, structs are value types, so have a little less overhead. Also, inheriting from `NSObject` is only used for classes that make use of some older objective C APIs.
In the end is not a big deal, I was just wondering if I had missed a specific reason why it was not just a struct:)
… On 5 Dec 2022, at 16:32, Luke Brandon Farrell ***@***.***> wrote:
@lukebrandonfarrell commented on this gist.
@nidegen <https://github.com/nidegen> Thanks! :) my swift is pretty rusty, what are the advantages to using a struct?
—
Reply to this email directly, view it on GitHub <https://gist.github.com/961a6dbc8367f0ac9cabc89b0052d1fe#gistcomment-4392270> or unsubscribe <https://github.com/notifications/unsubscribe-auth/ACC7NHZOOZF755SQWKAN7DDWLYDJDBFKMF2HI4TJMJ2XIZLTSKBKK5TBNR2WLJDHNFZXJJDOMFWWLK3UNBZGKYLEL52HS4DFQKSXMYLMOVS2I5DSOVS2I3TBNVS3W5DIOJSWCZC7OBQXE5DJMNUXAYLOORPWCY3UNF3GS5DZVRZXKYTKMVRXIX3UPFYGLK2HNFZXIQ3PNVWWK3TUUZ2G64DJMNZZDAVEOR4XAZNEM5UXG5FFOZQWY5LFVEYTCNRXGMZDCOBZU52HE2LHM5SXFJTDOJSWC5DF>.
You are receiving this email because you were mentioned.
Triage notifications on the go with GitHub Mobile for iOS <https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675> or Android <https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
No specific reason, I think it makes sense to be a struct tbh, you are right. When I get into this code again, which I will do soon as I am working on a library using this Gist. I can update it, thanks for pointing it out! :)
Thanks for sharing this, it is so helpful. Just wanna let you know that we have to swap the Width and Height when the orientation is Portrait.
@jmarkstar Interesting! How does that look in practice? Could you share a code snippet?
I think you forgot the import UIKit
. But really handy thank you!
@celian-m Very possible yes! You welcome :)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
First, nice writeup of the picker exif issue!
I was just wondering why you opted for an
NSObject
forExifData
. Wouldn't even astruct
be perfect here?