What I have to is just let user pick photo, upload it on server and then decode back and display it. What I am doing now is encode image and then pass it to a model as Base64. I store it as bytea in PosgtgreSQL.
let imageb: NSData = UIImageJPEGRepresentation(image, 0.7)! as NSData
let dataDecoded: String = imageb.base64EncodedString(options: .lineLength64Characters)
let imageStr: String = dataDecoded.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
then inside my model I have
private var JSON: [String: Any] {
get {
return ["data": self.imageData]
}
}
then I post it on server using Alamofire
APILayer.shared.request(parameters: self.JSON, url: "photo/create", method: .post) { responce in
//some handling stuff
}
server side client gateway method
let photos = user.getPhotos()
try! response.setBody(json: PhotoDocument.jsonFrom(array: photos))
object methods
func getPhotos() -> [PhotoDocument] {
return PhotoDocumentMapper.search(id: self.id)
}
private var JSON: [String: Any] {
get {
return ["description": self.description, "importancy": self.importancy, "date": self.date, "data": self.imageData, "id": self.id]
}
}
class func jsonFrom(array: [PhotoDocument]) -> [String: Any] {
var jsons: [Any] = []
for photo in array {
jsons.append(photo.JSON)
}
return ["photos" : jsons]
}
datamapper method
class func search(id: Int) -> [PhotoDocument] {
let p = PGConnection()
let _ = p.connectdb("host=localhost port=5432 dbname=perfect")
let result = p.exec(statement: "select * from \"Photos\" where \"userId\" = $1", params: [id])
p.close()
var photos: [PhotoDocument] = []
let resultsNumber = result.numTuples() - 1
if resultsNumber != -1 {
for index in 0...resultsNumber {
let id = result.getFieldInt(tupleIndex: index, fieldIndex: 0)!
let description = result.getFieldString(tupleIndex: index, fieldIndex: 4)!
let date = result.getFieldString(tupleIndex: index, fieldIndex: 5)!
let imageData = result.getFieldString(tupleIndex: index, fieldIndex: 3)!
let importancy = result.getFieldInt(tupleIndex: index, fieldIndex: 2)!
let userId = result.getFieldInt(tupleIndex: index, fieldIndex: 1)!
let photo = PhotoDocument(userId: userId, description: description, date: date, id: id, imageData: imageData, importancy: importancy)
photos.append(photo)
}
}
return photos
}
then I receive all this data, and I receive huge String and try this, but it crashes on the first line
let dataDecoded: NSData = NSData(base64Encoded: photo.imageData, options: .ignoreUnknownCharacters)! //crash here
let decodedimage = UIImage(data: dataDecoded as Data)
self.test.image = decodedimage
What am I doing wrong? How can I store UIImage as Base64String as bytea of PostgreSQL?
So what I had to do is remove this line while encoding
let imageStr: String = dataDecoded.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
and change PostgreSQL field to be text and not bytea
Related
I'm playing with Swift and JSONPlaceholder. I want to retrieve all the data contained in: https://jsonplaceholder.typicode.com/photos
I created a function that is acceding to the url, downloading the JSON but then I don't know how can I obtain the title and the thumbnailUrl to pass then for populate the tableView. In the past I used this code but now it's not working because on the JSONPlaceholder there are no array.
Any help for re-arrange the code for read and obtain the jsonplaceholder elements?
func loadList(){
let url = URL(string: urlReceived)
var myNews = NewInfo()
let task = URLSession.shared.dataTask(with: url!) {
(data, response, error) in
if error != nil{
print("ERROR")
}
else{
do {
if let content = data{
let myJson = try JSONSerialization.jsonObject(with: content, options: .mutableContainers)
//print(myJson)
if let jsonData = myJson as? [String : Any] {
if let myResults = jsonData["list"] as? [[String : Any]]{
//dump(myResults)
for value in myResults{
//Read time string from root
if let time = value ["dt_txt"] as? String{
myNews.time = time
}
//Read main container
if let main = value["main"]
as? [String : Any]{
if let temperature = main["temp"] as? Double {
myNews.temperature = String(temperature)
}
}
//Read from weather container
if let weather = value["weather"] as? [[String: Any]]{
for value in weather{
if let weatherContent = value["description"] as? String{
myNews.weatherDescription = weatherContent
}
}
}
self.myTableViewDataSource.append(myNews)
}
dump(self.myTableViewDataSource)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
}
catch{
}
}
}
task.resume()
}//End func
Okey, so with Alamofire + SwiftyJSON, you can do this:
func loadList(){
let url = "https://jsonplaceholder.typicode.com/photos"
AF.request(url).responseJSON { (response) in
switch response.result {
case .success(let value):
let json = JSON(value)
print(json)
for value in json.arrayValue {
let url = value.dictionaryValue["url"]!.stringValue
let albumId = value.dictionaryValue["albumId"]!.stringValue
let thumbnailUrl = value.dictionaryValue["thumbnailUrl"]!.stringValue
let id = value.dictionaryValue["id"]!.stringValue
let title = value.dictionaryValue["title"]!.stringValue
// Add this album to array.
let album = AlbumModel(id: id, albumId: albumId, title: title, thumbnailUrl: thumbnailUrl)
albums.append(album)
}
case .failure(let error):
print(error)
}
}
}
EDIT:
I made model for values
class AlbumModel {
var id: String?
var albumId: String?
var title: String?
var thumbnailUrl: String?
init(id: String?, albumId: String?, title: String?, thumbnailUrl: String?){
self.id = id
self.albumId = albumId
self.title = title
self.thumbnailUrl = thumbnailUrl
}
}
After that, just create an array like var albums = [AlbumModel]() and you can append all the albums to this. Easy to use after in tableViewcell (example: albums[indexPath.row].id)
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.
Function
import Foundation
struct Foods {
var fid: Int
var fname: String
var hits: Int?
var addr: String?
}
class Food {
func getFoodsById(_ fid: Int) -> [Foods]? {
var foods: Array<Foods>?
let URL_GET_TEAMS:String = "http://jsm0803.iptime.org:81/html/sufoo/getFoodById.php"
let requestURL = URL(string: URL_GET_TEAMS)
let request = NSMutableURLRequest(url: requestURL!)
request.httpMethod = "POST"
//request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let postParameters = "Fid=" + String(fid)
// "name="+teamName!+"&member="+memberCount!;
request.httpBody = postParameters.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest){data, response, error in
if error != nil{
print("error is \(error)")
return;
}
let dataString = String(data: data!, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) // 테스트용
//print(dataString!)
do{
var itemJSON: Dictionary<String, Any>!
itemJSON = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? Dictionary
let items:Array<Dictionary<String, Any>> = itemJSON["Food"] as! Array
for i in 0 ..< items.count{
var food: Foods
let item = items[i]
let fid:Int = item["Fid"] as! Int
let fname: String = item["Fname"] as! String
let hits: Int? = item["Hits"] as? Int
let delegate_image:String? = item["Delegate_Image"]as? String
food = Foods(fid: fid, fname: fname, hits: hits, addr: delegate_image)
foods?.append(food)
print("fid ->", food.fid)
print("fname ->", food.fname)
if let f = food.hits {
print("hits ->", f)
}
else{
print("hits ->", food.hits as Any)
}
if let f = food.addr {
print("delegate_image -> ", f)
}
else {
print("delegate_image -> ", food.addr as Any)
}
print("==============")
print("")
print ("fid ==== ", foods?.first?.fid)
print ("fid ==== ", foods?.last?.fid)
}
}catch {
print(error)
}
}
task.resume()
if let result = foods{
for r in result{
print ("r.fname")
print (r.fname)
}
}
print ("000000")
return foods
}
}
If I run this code in Xcode, I get the result below:
000000
fid -> 140
fname -> 밀 흑밀
hits -> nil
delegate_image -> ./pic_data/2309/20180423201954alj
==============
fid ==== nil
fid ==== nil
I want to return [var foods: Array?] value.
But although I made some values of Foods struct and used append function of Array in order to add Foods value into Array, it didn't works. There is no value in Array, only nil.(fid ==== nil)
Thus, it is useless to return that Array.
How can I get right results?
I need to get values like below:
fid ==== 140
fid ==== 140
Please help me to solve this problem.
I think I used Optional wrongly.
Main problem is you did not initiate your foods array.
//func getFoodsById(_ fid: Int) -> [Foods]? {
func getFoodsById(_ fid: Int) -> [Foods] {
//var foods: Array<Foods>?
var foods = [Foods]() // or var foods: [Foods] = []
You can initiate your array here and return empty list as a result if there is no food for the ID.
And rename your Foods struct with Food and Food class with something like FoodOperations. This would make more sense.
Read this for swift guides.
struct Food {
...
}
class FoodOperations {
...
}
if let error = error {
print("error is \(error)")
return;
}
print ("fid ==== ", foods.first?.fid ?? 0)
Better make a model class to store data. For example:
class Food {
var fid: Int
var fname: String
var hits: Int?
var addr: String?
}
Then after getting result do something like this :
var foodArray = [Foods]()
for item in items {
let food = Food()
guard let food.fid = item["Fid"] as? Int else {return}
foodArray.append(food)
}
print(foodArray)
And suggested by Francesco Deliro, use completion handler to return values when your for loop is done. In your case return statement is getting called before for loop ends.
Also don't do force unwrapping, try to use if let/ guard let .
You need to change your function implementation adding a completion handler, because your return is called before the for loop is ended:
func getFoodsById(_ fid: Int, completion: (([Foods]?, Error?) -> Void)?) {
//your precedent code
//then when you make the request call the completion
let task = URLSession.shared.dataTask(with: request as URLRequest){data, response, error in
guard error == nil else {
completion?(nil, error)
return
}
let dataString = String(data: data!, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) // 테스트용
//print(dataString!)
do{
var itemJSON: Dictionary<String, Any>!
itemJSON = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? Dictionary
let items:Array<Dictionary<String, Any>> = itemJSON["Food"] as! Array
for i in 0 ..< items.count{
var food: Foods
let item = items[i]
let fid:Int = item["Fid"] as! Int
let fname: String = item["Fname"] as! String
let hits: Int? = item["Hits"] as? Int
let delegate_image:String? = item["Delegate_Image"]as? String
food = Foods(fid: fid, fname: fname, hits: hits, addr: delegate_image)
foods?.append(food)
print("fid ->", food.fid)
print("fname ->", food.fname)
if let f = food.hits {
print("hits ->", f)
}
else{
print("hits ->", food.hits as Any)
}
if let f = food.addr {
print("delegate_image -> ", f)
}
else {
print("delegate_image -> ", food.addr as Any)
}
print("==============")
print("")
print ("fid ==== ", foods?.first?.fid)
print ("fid ==== ", foods?.last?.fid)
}
completion?(foods, nil)
}catch {
print(error)
completion?(nil, error)
}
}
task.resume()
}
And you can use it in this way:
//where you have to make the call
self.getFoodsById(yourId) { (foodsArray, error) in
//here you can manage your foods array
}
Declare Struct
struct AlertModel { var alert:String var title:String }
Create Variable
var alertData = AlertModel
Append data
let alert = AlertModel(alert: "Waring", title: "Over Load") alertData.append(alert)
print(alertData)
I have function for loop data(title, price, drug) JSON But I can't loop imageUrl to show in imageview on this function please help see my code.
This function barcodeReaded
func barcodeReaded(barcode: String) {
print("Barcode is: \(barcode)")
showCodeLabel.text = barcode
let data = NSData(contentsOfURL: episode.thumbnailURL!)
let image = UIImage(data: data!)
self.thumbnailImageView.image = image
let episodes = Episode.downloadAllEpisodes()
var filteredEpisodes = episodes.filter({ $0.testCode == barcode })
if filteredEpisodes.count > 0 {
titleLabel.text = filteredEpisodes[0].title
drugLabel.text = filteredEpisodes[0].drug
priceLabel.text = filteredEpisodes[0].price
//thumbnailImageView.image = filteredEpisodes[0].thumnailURL
}
}
This JSON file
{
"episode": [
{
"testCode": "11111111",
"title": "Stomachic mixture 180 ml",
"drug": "AAAAA",
"thumbnailURL": "https://firebasestorage.googleapis.com/v0/b/rxscan-a14ee.appspot.com/o/j01.jpg?alt=media&token=5718797b-fc9c-416e-9394-b544c2880dc9",
"price": "100"
},
{
"testCode": "22222222",
"title": "Parasetamol 200 ml",
"drug": "BBBBB",
"thumbnailURL": "urlImage",
"price": "150"
},
{
"testCode": "33333333",
"title": "Beramol 300 ml",
"drug": "CCCCC",
"thumbnailURL": "urlImage",
"price": "120"
}
]
}
This some code
import Foundation
class Episode
{
var title: String?
var thumbnailURL: NSURL?
var drug: String?
var price: String?
var testCode: String?
init(title: String, thumbnailURL: NSURL, drug: String, price: String, testCode: String)
{
self.title = title
self.thumbnailURL = thumbnailURL
self.drug = drug
self.price = price
self.testCode = testCode
}
typealias EpisodeDictionary = [String : AnyObject]
init(espDictionary: EpisodeDictionary)
{
self.title = espDictionary["title"] as? String
self.thumbnailURL = NSURL(string: espDictionary["thumbnailURL"] as! String)
self.drug = espDictionary["drug"] as? String
self.price = espDictionary["price"] as? String
self.testCode = espDictionary["testCode"] as? String
}
static func downloadAllEpisodes() -> [Episode]
{
var episodes = [Episode]()
let jsonFile = NSBundle.mainBundle().pathForResource("testJson3edit6", ofType: "json")
let jsonData = NSData(contentsOfFile: jsonFile!)
if let jsonDictionary = NetworkService.parseJSONFromData(jsonData) {
let espDictionaries = jsonDictionary["episodes"] as! [EpisodeDictionary]
for dict in espDictionaries {
let episode = Episode(espDictionary: dict)
episodes.append(episode)
}
}
return episodes
}
}
NetworkService.swift
import Foundation
class NetworkService
{
// TODO: Make this class be able to download images from a URL
lazy var configuration: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
lazy var session: NSURLSession = NSURLSession(configuration: self.configuration)
let url: NSURL
init(url: NSURL)
{
self.url = url
}
func downloadImage(completion: (NSData -> Void))
{
let request = NSURLRequest(URL: self.url)
let dataTask = session.dataTaskWithRequest(request) { (data, response, error) in
if error == nil {
if let httpResponse = response as? NSHTTPURLResponse {
switch (httpResponse.statusCode) {
case 200:
if let data = data {
completion(data)
}
default:
print(httpResponse.statusCode)
}
}
} else {
print("Error download data: \(error?.localizedDescription)")
}
}
dataTask.resume()
}
}
extension NetworkService
{
static func parseJSONFromData(jsonData: NSData?) -> [String : AnyObject]?
{
if let data = jsonData {
do {
let jsonDictionary = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers) as? [String : AnyObject]
return jsonDictionary
} catch let error as NSError {
print("Error processing json data: \(error.localizedDescription)")
}
}
return nil
}
}
This code for get image that use in detailViewController Which I don't know how to apply with function barcodeReaded.
if episode.thumbnailURL != nil {
if let thumbnailURL = episode.thumbnailURL {
let networkService = NetworkService(url: thumbnailURL)
networkService.downloadImage({ (data) in
//thumbnailImageView.image = episode.thumbnailURL
let image = UIImage(data: data)
dispatch_async(dispatch_get_main_queue(), {
self.thumbnailImageView.image = image
})
})
}
}
Basically with thumbnailImageView.image = episode.thumbnailURL your are trying to assign a String to an object of type UIImage.
You should do it:
1. Get NSURL from your json string
let url = NSURL(string: episode.thumbnailURL)!
2. Get the NSData of that NSURL
let data = NSData(contentsOfURL: url)!
3. Assign the UIImage from NSData
let image = UIImage(data: data)
self.thumbnailImageView.image = image
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