How to write Exif data to image in Swift with lat long - ios

I am trying to write EXIF data to the image but CGImageDestinationFinalize crashes:
var image = info[UIImagePickerControllerOriginalImage] as! UIImage
let jpeg = UIImageJPEGRepresentation(image, 1.0)
var source: CGImageSource? = nil
source = CGImageSourceCreateWithData((jpeg as CFData?)!, nil)
let metadata = CGImageSourceCopyPropertiesAtIndex(source!, 0, nil) as? [AnyHashable: Any]
var metadataAsMutable = metadata
var EXIFDictionary = (metadataAsMutable?[(kCGImagePropertyExifDictionary as String)]) as? [AnyHashable: Any]
var GPSDictionary = (metadataAsMutable?[(kCGImagePropertyGPSDictionary as String)]) as? [AnyHashable: Any]
GPSDictionary![(kCGImagePropertyGPSLatitude as String)] = 30.21313
GPSDictionary![(kCGImagePropertyGPSLongitude as String)] = 76.22346
EXIFDictionary![(kCGImagePropertyExifUserComment as String)] = "Hello Image"
let UTI: CFString = CGImageSourceGetType(source!)!
let dest_data = NSMutableData()
let destination: CGImageDestination = CGImageDestinationCreateWithData(dest_data as CFMutableData, UTI, 1, nil)!
CGImageDestinationAddImageFromSource(destination, source!, 0, (metadataAsMutable as CFDictionary?))
CGImageDestinationFinalize(destination)

Please check this below answer.
you got error due to nil value on EXIFDictionary and GPSDictionary
var image = info[UIImagePickerControllerOriginalImage] as! UIImage
let jpeg = UIImageJPEGRepresentation(image, 1.0)
var source: CGImageSource? = nil
source = CGImageSourceCreateWithData((jpeg as CFData?)!, nil)
let metadata = CGImageSourceCopyPropertiesAtIndex(source!, 0, nil) as? [AnyHashable: Any]
var metadataAsMutable = metadata
var EXIFDictionary = (metadataAsMutable?[(kCGImagePropertyExifDictionary as String)]) as? [AnyHashable: Any]
var GPSDictionary = (metadataAsMutable?[(kCGImagePropertyGPSDictionary as String)]) as? [AnyHashable: Any]
if !(EXIFDictionary != nil) {
EXIFDictionary = [AnyHashable: Any]()
}
if !(GPSDictionary != nil) {
GPSDictionary = [AnyHashable: Any]()
}
GPSDictionary![(kCGImagePropertyGPSLatitude as String)] = 30.21313
GPSDictionary![(kCGImagePropertyGPSLongitude as String)] = 76.22346
EXIFDictionary![(kCGImagePropertyExifUserComment as String)] = "Hello Image"
let UTI: CFString = CGImageSourceGetType(source!)!
let dest_data = NSMutableData()
let destination: CGImageDestination = CGImageDestinationCreateWithData(dest_data as CFMutableData, UTI, 1, nil)!
CGImageDestinationAddImageFromSource(destination, source!, 0, (metadataAsMutable as CFDictionary?))
CGImageDestinationFinalize(destination)

This could be from your destination definition.
This worked for me
(...)
let source = CGImageSourceCreateWithData(jpgData as CFData, nil)
let finalData = NSMutableData()
let destination = getDestination(finalData:finalData, source:source!)
(...)
// Note that :
// NSMutableData type variable will be cast to CFMutableData
//
fileprivate func getDestination(finalData:CFMutableData, source:CGImageSource)->CGImageDestination?{
guard let destination = CGImageDestinationCreateWithData(finalData,
CGImageSourceGetType(source)!,
1,
nil)else{return nil}
return destination
}

Related

Save two different images at the same time on firebase

I try to save two different images at the same time at one storage location
This is my function to save the information
var text: String = ""
var addedByUser: String?
var userImage: UIImage?
var jobImage: UIImage!
var downloadURL: String?
var userDownloadURL: String?
let ref: DatabaseReference!
init(text: String? = nil, jobImage: UIImage? = nil, addedByUser: String? = nil, userImage: UIImage? = nil) {
self.text = text!
self.jobImage = jobImage
self.addedByUser = addedByUser
self.userImage = userImage
ref = Database.database().reference().child("jobs").childByAutoId()
}
init(snapshot: DataSnapshot){
ref = snapshot.ref
if let value = snapshot.value as? [String : Any] {
text = value["text"] as! String
addedByUser = value["addedByUser"] as? String
downloadURL = value["imageDownloadURL"] as? String
userDownloadURL = value["imageUserDownloadURL"] as? String
}
}
func save() {
let newPostKey = ref.key
// save jobImage
if let imageData = jobImage.jpegData(compressionQuality: 0.5) {
let storage = Storage.storage().reference().child("jobImages/\(newPostKey)")
storage.putData(imageData).observe(.success, handler: { (snapshot) in
self.downloadURL = snapshot.metadata?.downloadURL()?.absoluteString
let postDictionary = [
"imageDownloadURL" : self.downloadURL!,
"imageUserDownloadURL" : self.userDownloadURL!,
"text" : self.text,
"addedByUser" : self.addedByUser!
] as [String : Any]
self.ref.setValue(postDictionary)
})
}
}
I tried following code
if let imageData = jobImage.jpegData(compressionQuality: 0.5), ((userImage?.jpegData(compressionQuality: 0.5)) != nil) {
But it's not working as then nothing get's saved in the database...
Do you have any ideas how I can solve it?
I believe the question is how do I upload an image to two different locations. It's unclear why there's an observe function so this answer ignores that as it may not be needed.
Starting with your code, your save function will look like this
func save() {
self.uploadImageTask(imageName: "my_image.png", toLocation: "jobImage")
self.uploadImageTask(imageName: "my_image.png", toLocation: "anotherLocation")
}
and then the upload function
func uploadImageTask(imageName: String, toLocation: String) {
let theImage = UIImage(named: imageName) //set up your image here
let data = UIImagePNGRepresentation(theImage)! //we're doing a PNG
let storage = Storage.storage()
let storageRef = storage.reference()
let locationRef = storageRef.child("images").child(toLocation)
let imageLocationRef = locationRef.child(imageName)
// Upload the file to the path "images/location/imageName"
let uploadTask = locationRef.putData(data, metadata: nil) { (metadata, error) in
guard let metadata = metadata else {
print("error while uploading")
return
}
let size = metadata.size // Metadata contains file metadata such as size, content-type.
print(size)
locationRef.downloadURL { (url, error) in
guard let downloadURL = url else {
print("an error occured after uploading and then downloading")
return
}
let x = downloadURL.absoluteString
print(x) //or build a dict and save to Firebase
}
}
}
the result is an image stored at
/images/jobImage/my_image.png
/images/anotherLocation/my_image.png
and it will also print the path to each image, which could be stored in Firebase.

Showing multiple location using 'MKMapView'

I am trying to show multiple locations which saved in Mysql using the code below. The data is loading but I have no idea how to show multiple locations depending on latitude and longitude.
Mysql is connected to application via PHP file.
Here is my code, the part which I called from NSObject:
func downloadItems() {
// the download function
// return the nsuserdefaults which hold the lati and longi from the notification table
UserDefaults.standard.string(forKey: "test");
let myUrl = UserDefaults.standard.string(forKey: "test");
let urlPath: String = myUrl!
let url: URL = URL(string: urlPath)!
let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: url) { (data, response, error) in
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON(data!)
}
}
task.resume()
}
func parseJSON(_ data:Data) {
var jsonResult = NSArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
} catch let error as NSError {
print(error)
}
var jsonElement = NSDictionary()
let locations = NSMutableArray()
for i in 0 ..< jsonResult.count
{
jsonElement = jsonResult[i] as! NSDictionary
let location = LocationModel()
//the following insures none of the JsonElement values are nil through optional binding
if let evIdL = jsonElement["id"] as? String,
let evUserNameL = jsonElement["username"] as? String,
let evNotikindL = jsonElement["notikind"] as? String,
let evLatiL = jsonElement["lati"] as? String,
let evLongiL = jsonElement["longi"] as? String,
let evLocatL = jsonElement["locat"] as? String,
let evTimedateL = jsonElement["timedate"] as? String,
let evDistanceL = jsonElement["distance"] as? String
{
location.evId = evIdL
location.evUsername = evUserNameL
location.evNotikind = evNotikindL
location.evLati = evLatiL
location.evLongi = evLongiL
location.evLocat = evDistanceL
location.evTimedate = evTimedateL
location.evDisatnce = evDistanceL
location.evLocat = evLocatL
}
locations.add(location)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.itemsDownloaded(items: locations)
})
}
}
I have no idea how to show few location on map.
try this code....
var locations = NSMutableArray()
var mapView = GMSMapView()
for i in 0..< location.count{
let obj = location[i]
lat = obj["lati"] as? Double
lng = obj["longi"] as? Double
let markerPoint = GMSMarker()
markerPoint.position = CLLocationCoordinate2D(latitude: lat!, longitude: lng!)
markerPoint.iconView = self.avtarImage() // Your image name
markerPoint.map = mapView // your mapview object name
markerPoint.zIndex = Int32(i)
markerPoint.infoWindowAnchor = CGPoint(x: 0, y: 0)
markerPoint.accessibilityLabel = String(format: "%d", i)
}

how to parse the data from web services and pass it to table view?

In this the web services i need to save and pass it to a table view how to implement this and the getting data also not possible from json and i need to display it on table view as mentioned below in image can anyone help me how to implement this or any suggestion also would be of great help
and my code is shown below
let url = "http://www.json-generator.com/api/json/get/cwqUAMjKGa?indent=2"
var detailsArray :[[String: AnyObject]] = []
var titleName = [String]()
var productName = [String]()
var children = [String]()
var childrenArray :[[String: AnyObject]] = []
var productsArray :[[String:AnyObject]] = []
var name = [String]()
func downloadJsonWithURL() {
let url = NSURL(string: self.url)
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
self.detailsArray = (jsonObj!.value(forKey: "data") as? [[String: AnyObject]])!
print(self.detailsArray)
for item in self.detailsArray{
if let detailDict = item as? NSDictionary {
if let name = detailDict.value(forKey: "name"){
self.titleName.append(name as! String)
print(self.productName)
}
self.childrenArray = (detailDict.value(forKey: "children") as? [[String : AnyObject]])!
for item in self.childrenArray {
if let detailDict = item as? NSDictionary {
if let name = detailDict.value(forKey: "name"){
self.productName.append(name as! String)
}
self.productsArray = (detailDict.value(forKey: "products") as? [[String : AnyObject]])!
for item in self.productsArray{
if let detailDict = item as? NSDictionary {
if let name = detailDict.value(forKey: "name"){
self.name.append(name as! String)
print(self.name)
}
}
}
}
}
}
}
OperationQueue.main.addOperation({
print(self.productName)
print(self.titleName)
print(self.name)
})
}
}).resume()
}

Ambiguous reference to member 'subscript' when using Google maps route

The below code works fine in Xcode 7.1 but when I upgrade to Xcode 8 and swift 3 I get this error, I had searched on internet for same problem in stackoverflow but none of them answer my question.
func drawRoute() {
clearRoute()
// self.routePolyline = nil
// self.routePolyline.map = nil
let route = mapTk.overviewPolyline!["points"] as String //error here
//var overViewPolyLine = routes![0]["overview_polyline"]["points"].string
let path: GMSPath = GMSPath(fromEncodedPath: route)
routePolyline = GMSPolyline(path: path)
routePolyline.map = viewMap
}
here is the class mapTK for more info:
import Foundation
import UIKit
import CoreLocation
import MapKit
///This Class is designed for the detail methods that contact google maps server like GeoCode, GetDirection and Calculate distance and time
class MapTK: NSObject {
let baseURLGeocode = "https://maps.googleapis.com/maps/api/geocode/json?"
var lookupAddressResults: Dictionary<NSObject, AnyObject>!
var fetchedFormattedAddress: String!
var fetchedAddressLongitude: Double!
var fetchedAddressLatitude: Double!
let baseURLDirections = "https://maps.googleapis.com/maps/api/directions/json?"
var selectedRoute: Dictionary<NSObject, AnyObject>!
var overviewPolyline: Dictionary<NSObject, AnyObject>!
var originCoordinate: CLLocationCoordinate2D!
var destinationCoordinate: CLLocationCoordinate2D!
var originAddress: String!
var destinationAddress: String!
var totalDistanceInMeters: UInt = 0
var totalDistance: String!
var totalDurationInSeconds: UInt = 0
var totalDuration: String!
/* private let errorDictionary = ["NOT_FOUND" : "At least one of the locations specified in the request's origin, destination, or waypoints could not be geocoded",
"ZERO_RESULTS":"No route could be found between the origin and destination",
"MAX_WAYPOINTS_EXCEEDED":"Too many waypointss were provided in the request The maximum allowed waypoints is 8, plus the origin, and destination",
"INVALID_REQUEST":"The provided request was invalid. Common causes of this status include an invalid parameter or parameter value",
"OVER_QUERY_LIMIT":"Service has received too many requests from your application within the allowed time period",
"REQUEST_DENIED":"Service denied use of the directions service by your application",
"UNKNOWN_ERROR":"Directions request could not be processed due to a server error. Please try again"]
*/
override init() {
super.init()
}
///the geocodeAddress is getting information from googlemaps with JSON it receive the geocode information
func geocodeAddress(address: String!, withCompletionHandler completionHandler: #escaping ((_: String, _: Bool) -> Void)) {
if let lookupAddress = address {
var geocodeURLString = baseURLGeocode + "address=" + lookupAddress
//geocodeURLString = geocodeURLString.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
let geocodeURL = NSURL(string: geocodeURLString)
// dispatch_Dispatch.Queue.mainasync(dispatch_get_main_queue(), { () -> Void in
DispatchQueue.main.async {
let geocodingResultsData = NSData(contentsOf: geocodeURL! as URL)
// let error: NSError?
do
{
let dictionary = try JSONSerialization.jsonObject(with: geocodingResultsData! as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
// try NSJSONSerialization.JSONObjectWithData(directionsData!, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
/*
if (error != nil) {
print(error)
completionHandler(status: "", success: false)
}
*/
// else {
// Get the response status.
let status = dictionary?["status"]as! String
// let status = dictionary["status"] as! [AnyObject]
if status == "OK" {
let allResults = dictionary?["results"] as! Array<Dictionary<NSObject, AnyObject>>
self.lookupAddressResults = allResults[0]
// Keep the most important values.
self.fetchedFormattedAddress = self.lookupAddressResults["formatted_address"]? as? String
let geometry = self.lookupAddressResults["geometry"] as! Dictionary<NSObject, AnyObject>
self.fetchedAddressLongitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lng"] as! NSNumber).doubleValue
self.fetchedAddressLatitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lat"] as! NSNumber).doubleValue
completionHandler(status, true)
}
else {
completionHandler(status, false)
}
} catch let error as NSError {
print(error)
}
}
}
else {
completionHandler("No valid address.", false)
}
}
///getDirection method is using JSON to receive waypoints for making route for drawing polyline, as its name suggests it get the direciton inforamtion, first it send lat and lng the receive a dictionary and that dictionary give all the infrmation need to draw route like waypoints
func getDirections(origin: String!, destination: String!, waypoints: Array<String>!, travelMode: TravelModes!, completionHandler: #escaping ((_ status: String, _ success: Bool) -> Void)) {
if let originLocation = origin {
if let destinationLocation = destination {
var directionsURLString = baseURLDirections + "origin=" + originLocation + "&destination=" + destinationLocation //+ "&key=AIzaSyDsDqj0EMYZ-C4lGF3tmbntZtzurLl6_J4"
if let routeWaypoints = waypoints {
directionsURLString += "&waypoints=optimize:true"
for waypoint in routeWaypoints {
directionsURLString += "|" + waypoint
}
}
if let _ = travelMode {
var travelModeString = ""
switch travelMode.rawValue {
case TravelModes.walking.rawValue:
travelModeString = "walking"
case TravelModes.bicycling.rawValue:
travelModeString = "bicycling"
default:
travelModeString = "driving"
}
directionsURLString += "&mode=" + travelModeString
}
// directionsURLString = directionsURLString.stringByAddingPercentEncodingWithAllowedCharacters(NSUTF8StringEncoding)!
// directionsURLString = directionsURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
// directionsURLString = directionsURLString.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
/// directionsURLString = directionsURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
directionsURLString = directionsURLString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
//let encodedHost = unencodedHost.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
//var url = NSURL(urlString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()))
//let savePath = (documentDirectory as NSString).stringByAppendingPathComponent("mergeVideo-\(date).mov")
let directionsURL = NSURL(string: directionsURLString)
// DispatchQueue.main.asynchronously(execute: { () -> Void in
DispatchQueue.main.async {
let directionsData = NSData(contentsOf: directionsURL! as URL)
// var error: NSError?
do {
let dictionary = try JSONSerialization.jsonObject(with: directionsData! as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
/*
if (error != nil) {
print(error)
completionHandler(status: "", success: false)
}
*/
// else {
let status = dictionary?["status"] as! String
if status == "OK" {
self.selectedRoute = (dictionary?["routes"] as! Array<Dictionary<NSObject, AnyObject>>)[0]
self.overviewPolyline = self.selectedRoute["overview_polyline"] as! Dictionary<NSObject, AnyObject>
let legs = self.selectedRoute["legs"] as! Array<Dictionary<NSObject, AnyObject>>
let startLocationDictionary = legs[0]["start_location"] as! Dictionary<NSObject, AnyObject>
self.originCoordinate = CLLocationCoordinate2DMake(startLocationDictionary["lat"] as! Double, startLocationDictionary["lng"] as! Double)
let endLocationDictionary = legs[legs.count - 1]["end_location"]as! Dictionary<NSObject, AnyObject>
self.destinationCoordinate = CLLocationCoordinate2DMake(endLocationDictionary["lat"] as! Double, endLocationDictionary["lng"] as! Double)
self.originAddress = legs[0]["start_address"] as! String
self.destinationAddress = legs[legs.count - 1]["end_address"] as! String
self.calculateTotalDistanceAndDuration()
completionHandler(status, true)
}
else {
completionHandler(status, false)
}
} catch let error as NSError {
print(error)
}
}
}
else {
completionHandler("Destination is nil.", false)
}
}
else {
completionHandler("Origin is nil", false)
}
}
/// this method below is for the calculation of Distance adn Duration of each location for travel
func calculateTotalDistanceAndDuration() {
let legs = self.selectedRoute["legs"] as! Array<NSDictionary>
totalDistanceInMeters = 0
totalDurationInSeconds = 0
for leg in legs {
totalDistanceInMeters += (leg["distance"] as! Dictionary<NSObject, AnyObject>)["value"] as! UInt
totalDurationInSeconds += (leg["duration"]as! Dictionary<NSObject, AnyObject>)["value"] as! UInt
}
let distanceInKilometers: Double = Double(totalDistanceInMeters / 1000)
totalDistance = "Total Distance: \(distanceInKilometers) Km"
let mins = totalDurationInSeconds / 60
let hours = mins / 60
let days = hours / 24
let remainingHours = hours % 24
let remainingMins = mins % 60
let remainingSecs = totalDurationInSeconds % 60
totalDuration = "Duration: \(days) d, \(remainingHours) h, \(remainingMins) mins, \(remainingSecs) secs"
}
}
Use
Dictionary<String, Any>
as JSON dictionary type.
JSON keys are required to be String anyway and
AnyObject has been changed to Any in Swift 3.
Edit :
I recommend to use a type alias for the JSON dictionary type
typealias JSONObject = [String:Any] // synonym of Dictionary<String, Any>
Then you can write
var lookupAddressResults: JSONObject!
...
var selectedRoute: JSONObject!
var overviewPolyline: JSONObject!
and the parsing code
...
if status == "OK" {
let allResults = dictionary?["results"] as! Array<JSONObject>
self.lookupAddressResults = allResults[0]
// Keep the most important values.
self.fetchedFormattedAddress = self.lookupAddressResults["formatted_address"] as? String
let geometry = self.lookupAddressResults["geometry"] as! JSONObject
self.fetchedAddressLongitude = ((geometry["location"] as! JSONObject)["lng"] as! NSNumber).doubleValue
self.fetchedAddressLatitude = ((geometry["location"] as! JSONObject)["lat"] as! NSNumber).doubleValue
completionHandler(status, true)
}
...
You can also replace
( ... as! NSNumber).doubleValue
with
... as! Double

Swift accessing EXIF Dictionary

Info: Using Swift and the CGImageSourceCreateWithURL function.
I am attempting to load a file from a URL and then edit a dictionary which has all the data from that particular photo.
This is the code from the .swift file.
let url = NSURL(string: "http://jwphotographic.co.uk/Images/1.jpg")
let imageSource = CGImageSourceCreateWithURL(url, nil)
let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as Dictionary
println(imageProperties)
//this is an example
let aperture = imageProperties[kCGImagePropertyGPSLatitude] as! NSNumber!
/*
//these are all being defined as nil
//Load the ones from the exif data of the file
let lensUsed = imageProperties[kCGImagePropertyExifFocalLength]
let aperture = imageProperties[kCGImagePropertyExifApertureValue] as!
let isoSpeed = imageProperties[kCGImagePropertyExifISOSpeedRatings] as! NSNumber
let latitude = imageProperties[kCGImagePropertyGPSLatitude] as! NSNumber
let longitude = imageProperties[kCGImagePropertyGPSLongitude] as! NSNumber
let shutterSpeed = imageProperties[kCGImagePropertyExifShutterSpeedValue] as! NSNumber
let cameraName = imageProperties[kCGImagePropertyExifBodySerialNumber] as! NSNumber
*/
println(aperture)
Even though image properties prints all the data as would be expected, no-matter what I have attmpted to extract from the imageProperties dictionary - it is always returned as null - such as 'aperture' in the example. The imageProperties prints as;
[{TIFF}: {
Artist = JOHN;
Copyright = "johnrwatson0#gmail.com";
DateTime = "2015:07:31 21:07:05";
Make = Canon;
Model = "Canon EOS 7D Mark II";
ResolutionUnit = 2;
Software = "Adobe Photoshop Lightroom 6.0 (Macintosh)";
XResolution = 72;
YResolution = 72;
}, {IPTC}: {
Byline = (
JOHN
);
CopyrightNotice = etc.. etc..
I have done a lot of research and testing and I simply cannot work out what I'm doing wrong to access the elements in this dictionary - Could someone give me an example how I would set a variable as the "Model" element inside the dictionary?
In Swift 3.0 I found the following solution
let url = NSURL(string: "http://jwphotographic.co.uk/Images/1.jpg")
let imageSource = CGImageSourceCreateWithURL(url, nil)
let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as Dictionary?
let exifDict = imageProperties?[kCGImagePropertyExifDictionary]
Now you can access the exif-Tags by for example
let dateTimeOriginal = exifDict?[kCGImagePropertyExifDateTimeOriginal]
Swift.print("dateTimeOriginal: \(dateTimeOriginal)")
You will get optional values and you have to test, if there are values or nil. For a list of available property constants look at the apple documentation.
To get to Exif parameters do the following:
let filePath_ = "file:///Users/pfernandes/Documents/softwareDevelopment/PhotoLine/TestData/IMG_1243.JPG"
let fileURL = NSURL(string:filePath_)
let myImage = CGImageSourceCreateWithURL(fileURL!,nil)
let imageDict = CGImageSourceCopyPropertiesAtIndex(myImage!, 0, nil)! as NSDictionary;
let tiffModel_ = imageDict.value(forKey: "{TIFF}")
let cameraMake = (tiffModel_ as AnyObject).value(forKey: kCGImagePropertyTIFFMake as String)
let cameraModel = (tiffModel_ as AnyObject).value(forKey: kCGImagePropertyTIFFModel as String)
let exifDict_ = imageDict.value(forKey: "{Exif}") as! NSDictionary
let dateTimeOriginal = exifDict_.value(forKey:kCGImagePropertyExifDateTimeOriginal as String) as! NSString
let exposure = exifDict_.value(forKey:kCGImagePropertyExifExposureTime as String)
print ("\(String(describing: cameraMake)) \(String(describing: cameraModel)) \(dateTimeOriginal) \(exposure!)")
This code will help you to obtain the date of the photo and exif
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true, completion: nil)
if let image = info[.originalImage] as? UIImage {
let assetURL = info[UIImagePickerController.InfoKey.referenceURL] as! NSURL
let asset = PHAsset.fetchAssets(withALAssetURLs: [assetURL as URL], options: nil)
guard let result = asset.firstObject else {
return
}
let imageManager = PHImageManager.default()
imageManager.requestImageData(for: result , options: nil, resultHandler:{
(data, responseString, imageOriet, info) -> Void in
let imageData: NSData = data! as NSData
if let imageSource = CGImageSourceCreateWithData(imageData, nil) {
let imageDict = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil)! as NSDictionary
let exifDict_ = imageDict.value(forKey: "{Exif}") as! NSDictionary
let dateTimeOriginal = exifDict_.value(forKey:kCGImagePropertyExifDateTimeOriginal as String) as! NSString
print (" exifDict : \(exifDict_)")
print (" fecha : original \(dateTimeOriginal)")
}
})
picker.dismiss(animated: true, completion: nil)
}
Here is a sample code that helps us in reading the entire imageMetadata and converts it into dictionary format.
guard let imageSource = CGImageSourceCreateWithURL(fileURL as CFURL, nil),
let metadata = CGImageSourceCopyMetadataAtIndex(imageSource, 0, nil),
let tags = CGImageMetadataCopyTags(metadata),
let imageInfo = self.readMetadataTagArr(tagArr: tags) else { return }
Then there are a few helper functions that actually do all the hardwork to extract/convert the data into dictionary format.
/// Reads the Arrays of tags and convert them into a dictionary of [String: Any]
private static func readMetadataTagArr(tagArr: CFArray) -> [String: Any]? {
var result = [String: Any]()
for (_, tag) in (tagArr as NSArray).enumerated() {
let tagMetadata = tag as! CGImageMetadataTag
if let cfName = CGImageMetadataTagCopyName(tagMetadata) {
let name = String(cfName)
result[name] = self.readMetadataTag(metadataTag: tagMetadata)
}
}
return result
}
/// convert CGImageMetadataTag to a dictionary of [String: Any]
private static func readMetadataTag(metadataTag: CGImageMetadataTag) -> [String: Any] {
var result = [String: Any]()
guard let cfName = CGImageMetadataTagCopyName(metadataTag) else { return result }
let name = String(cfName)
let value = CGImageMetadataTagCopyValue(metadataTag)
/// checking the type of `value` object and then performing respective operation on `value`
if CFGetTypeID(value) == CFStringGetTypeID() {
let valueStr = String(value as! CFString)
result[name] = valueStr
} else if CFGetTypeID(value) == CFDictionaryGetTypeID() {
let nsDict: NSDictionary = value as! CFDictionary
result[name] = self.getDictionary(from: nsDict)
} else if CFGetTypeID(value) == CFArrayGetTypeID() {
let valueArr: NSArray = value as! CFArray
for (_, item) in valueArr.enumerated() {
let tagMetadata = item as! CGImageMetadataTag
result[name] = self.readMetadataTag(metadataTag: tagMetadata)
}
} else {
// when the data was of some other type
let descriptionString: CFString = CFCopyDescription(value);
let str = String(descriptionString)
result[name] = str
}
return result
}
/// Converting CGImage Metadata dictionary to [String: Any]
private static func getDictionary(from nsDict: NSDictionary) -> [String: Any] {
var subDictionary = [String: Any]()
for (key, val) in nsDict {
guard let key = key as? String else { continue }
let tempDict: [String: Any] = [key: val]
if JSONSerialization.isValidJSONObject(tempDict) {
subDictionary[key] = val
} else {
let mData = val as! CGImageMetadataTag
let tempDict: [String: Any] = [key: self.readMetadataTag(metadataTag: mData)]
if JSONSerialization.isValidJSONObject(tempDict) {
subDictionary[key] = tempDict
}
}
}
return subDictionary
}
Here is the list of all dictionary keys extracted
Here are some sample dictionary values

Resources