Get info JSON Google Books - IOS - ios

how can I get authors from a google's book with JSON in Swift? This code works but I have a problem with authors.
This is an example of JSON:
{
"kind": "books#volume",
"id": "17BYrgEACAAJ",
"etag": "snlcLszwWRo",
"selfLink": "https://www.googleapis.com/books/v1/volumes/17BYrgEACAAJ",
"volumeInfo": {
"title": "Scomparso. Dave Brandstetter mysteries",
"authors": [
"Joseph Hansen"
],
"publisher": "Elliot",
"publishedDate": "2012-01",
func getBookInfo:
func getBookInfo(isbn: String) {
let url_book = "https://www.googleapis.com/books/v1/volumes?q=isbn:" + isbn
if let url = NSURL(string: url_book) {
NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: {data, _, error -> Void in
if let error = error {
print(error.localizedDescription)
} else {
let data = data,
jsonResult = try? NSJSONSerialization.JSONObjectWithData(data!, options: []),
arrayOfTitles = jsonResult!.valueForKeyPath("items.volumeInfo.title") as? [String],
arrayOfAuthors = jsonResult!.valueForKeyPath("items.volumeInfo.authors") as? [String],
arrayOfPublishers = jsonResult!.valueForKeyPath("items.volumeInfo.publisher") as? [String],
arrayUrlImageBook = jsonResult!.valueForKeyPath("items.volumeInfo.imageLinks.smallThumbnail") as? [String]
self.titles = arrayOfTitles!.joinWithSeparator(", ")
self.authors = arrayOfAuthors!.joinWithSeparator(", ")
self.publishers = arrayOfPublishers!.joinWithSeparator(", ")
let url_image_book: String! = arrayUrlImageBook!.joinWithSeparator(", ")
self.title_label.text = self.titles
self.author_label.text = self.authors
if self.publishers != nil{
self.publisher_label.text = self.publishers
}
self.book_image.downloadedFrom(url_image_book!)
}
}).resume()
}
}
Thanks in advance.

So in order to retrieve the authors, I do something similar but a little bit different.
First, I just create an array of all the books that are returned by the API for the URL that I send to it...
if let volumeInfo = jsonResult.valueForKeyPath("items.volumeInfo") as? [AnyObject] {
if volumeInfo.count != 0 {
self.volumeInfo = volumeInfo
self.translateJSONData() //here is where I then translate the data
}
}
Then in my data translation function, I loop through all the results, and get the values I want.
for apiResponse in self.volumeInfo {
if let singleVolumeInfo = apiResponse as? [String : AnyObject] {
let bookCategory = singleVolumeInfo["categories"] as? [String]
let bookAuthors = singleVolumeInfo["authors"] as? [String]
//more information here
}
}
But that code to get the authors will give you an array of all the author's names.

Related

How to Get JSON array which will be later stored in Realm table?

This is part of JSON my API returns, I don't know how to get that and store in Realm table?
"images": {
"image1": {
"url": "http://www.example.com/900150983cd24fb0d6963f7d28e17f72.gif",
"path": "data/2clb91218/900150983aassfb0d6963f7d28e17f72.gif",
"alt": "YWJj"
},
"image2": {
"url": "http://example/tex/fbce4a1ebe576539394e9493e30c7e5e.gif",
"path": "data/2clb91218/900150983cd24fb0d6963f7d28e332g22.gif",
"alt": "Yaaase22"
},
"image3": {
"url": "https://example.amazonaws.com/card/480_300/9/9jpul1218.jpg",
"path": "data/2clb91218/ewfregwrgw6963f7d28e17f72.gif",
"alt": "pic3"
}
}
NOTE : Dont test these urls, I didnt want to put real ones.
NOTE : In object 'Images', child-object's names are chaning so we have: 'image1' , 'image2' etc.
I have read on Stackoverflow that for storing arrays in RealmTable, we need to use non-dynamic List<> , so this is how I store image-urls in class:
var images = List<String>()
This is part of my responseJSON function where I am trying to Get images from API, 'continue' is there just to Get another objects if images are unable to Get :
guard let images = data["images"] as? [String: Any?],
let image = images["image1"] as? [String: String],
let imageUrl = image["url"] as? String
else { print("error") ; continue }
This above works for 'image1', but what about other image names, how to handle them?
And one more problem - 'Images' object can return NIL if there are no images which can aslo happen. If it's nil, I want to put "null" in RealmTable's column 'images' of the type I mentioned above.
I hope you can fully understand me, if not, comment and I will try to explain better.
You need
guard let images = data["images"] as? [String:[String:Any]] else { return }
let res = Array(images.values)
let urls = res.compactMap { $0["url"] as? String }
print("All urls : ",urls)
var images = List<String>()
let imagesDictInfo = responseData.toDictionary()
if let imageArray = imagesDictInfo["Images"] as? [[String:Any]]{
let imageArrayData = image.getJson()
//Store this ImageArray into realm or
for image in imageArray{
let imageData = image.getJson()
//Store Image Data
images.append(imageData)
}
}
func getJson(_ jsonString:String) -> [String:Any]?{
do{
let data = jsonString.data(using: .utf8)
if let json = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any] {
return json
}else{
return nil
}
}catch{
return nil
}
}
func toDictionary() -> NSDictionary {
let blankDict : NSDictionary = [:]
if let data = self.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as! NSDictionary
} catch {
print(error.localizedDescription)
}
}
return blankDict
}

Swift Compact Map returning empty

Hi I am trying to learn RXSwift and First time I came across these concepts like Maps and Compact Maps.
I am able to get the response, but this line always returns empty.
objects.compactMap(DummyUser.init)
fileprivate let Users = Variable<[DummyUser]>([])
fileprivate let bag = DisposeBag()
response
.filter { response, _ in
return 200..<300 ~= response.statusCode
}
.map { _, data -> [[String: Any]] in
guard (try? JSONSerialization.jsonObject(with: data, options: [])) != nil else {
return []
}
let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]
// print(json!["results"])
return json!["results"] as! [[String : Any]]
}
.filter { objects in
return objects.count > 0
}
.map { objects in
// objects.forEach{print($0["name"]!)}
let names = objects.map { $0["name"]!}
print(names)
return objects.compactMap(DummyUser.init)
}
.subscribe(onNext: { [weak self] newEvents in
self?.processEvents(newEvents)
})
.disposed(by: bag)
func processEvents(_ newEvents: [DummyUser]) {
var updatedEvents = newEvents + Users.value
if updatedEvents.count > 50 {
updatedEvents = Array<DummyUser>(updatedEvents.prefix(upTo: 50))
}
Users.value = updatedEvents
DispatchQueue.main.async {
self.MianUsertable.reloadData()
}
// refreshControl?.endRefreshing()
let eventsArray = updatedEvents.map{ $0.dictionary } as NSArray
eventsArray.write(to: userFileURL, atomically: true)
}
My Json Response is Here
https://randomuser.me/api/?results=5
DummyUser Class
import Foundation
typealias AnyDict = [String: Any]
class DummyUser {
let gender: String
let name: AnyDict
let dob: String
let picture: AnyDict
init?(dictionary: AnyDict) {
guard let Dgender = dictionary["gender"] as? String,
let Dname = dictionary["name"] as? AnyDict,
let birthdata = dictionary["dob"] as? AnyDict,
let Ddob = birthdata["dob"] as? String,
let Dpicture = dictionary["picture"] as? AnyDict
else {
return nil
}
gender = Dgender
name = Dname
dob = Ddob
picture = Dpicture
}
var dictionary: AnyDict {
return [
"user": ["name" : name, "gender": gender, "dob": dob],
"picture" : ["userImage": picture]
]
}
}
In your DummyUser model you are using failable initializer, so in case of wrong dictionary provided to init method it will return nil.
compactMap automatically automatically filters nil's and that's the reason why your output is empty.
Looking at this piece of code:
let names = objects.map { $0["name"]!}
return objects.compactMap(DummyUser.init)
I would debug this variable called names because it probably has wrong input for the DummyUser initializer. It should be dictionary containing all of your DummyUser parameters. You can also debug your failable initializer to see which of the parameter is missing.

How to cast this dictionary to an array or to get access to the values? {x = "1.1"; y = "2.3";}

I am using an api to get some data. The json has this data structure in one of its fields.
Here is the code to get access to my json:
let myUrl = NSURL(string:"http://openmensa.org/api/v2/canteens/\(choosenID)/days/" + currentDate + "/meals")!
URLSession.shared.dataTask(with: myUrl as URL) { (data, response, error) in
if error != nil {
print(error!)
} else {
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: []) as? [[String:Any]] {
print(json)
for entry in json {
if let userId = entry["id"], let name = entry["name"], let category = entry["category"], let price = entry["prices"], let notes = entry["notes"] {
var meal = MealObject(id:userId as! Int, name:name as! String as! String, category:category as! String, price:0.0, notes: notes as! [String]);
print(price)
// DO MY OTHER STUFF...
}
}
} else {
print("JSON is not an array of dictionaries")
}
} catch let error as NSError {
print(error)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}.resume()
print(json):
[["name": Macaroni & Cheese, "id": 2915045, "category": Tagesgericht 3, "prices": {
employees = "2.6";
others = "3.1";
pupils = "<null>";
students = "1.9";
}, "notes": <__NSArrayI 0x600000298380>(
Schwefeldioxid und Sulfite,
Senf,
Milch und Laktose,
Weizen,
Glutenhaltiges Getreide,
Alkohol,
mit Farbstoff
)
],
print(price):
{
employees = "1.9";
others = "2.4";
pupils = "<null>";
students = 1;
},
{
employees = "2.6";
others = "3.1";
pupils = "<null>";
students = "1.9";
}
I have no problem to get access to the id, the category, or the notes! The only problem are the prices.
How can I get access to this data structure? I want to save the double values to an array.
Paulw11 delivered the answer in his comment:
My unknown data structure is a dictionary inside the json.
I changed my code from this:
if let userId = entry["id"], let price = entry["prices"]
to this:
if let userId = entry["id"], let price = entry["prices"] as? [String:Any]]
Now I can use it like this:
var employer = price["employees"]
Thanks to Paulw11;)
Did you try using "SwiftyJSON" Cocoapods? If not, I would recommend installing "SwiftyJSON" .Then its as easy as shown below
let json = data
let employees = json["employees"]
let others = json["others"]
let pupils = json["pupils"]
let students = json["students"]
if you are interested, you can go through this link https://cocoapods.org/?q=swiftyjsYou may need to go through this if you don't know about cocoapods usage or installation

Fetching every object in nested JSON

So here you can see my method which fetches JSON. My problem is that I want my loop to go through every object. Not one object 10 times like it is now. I know that player["1"] is causing it to fetch first object repeatedly, it is just for the sake of the example. I need to get every player information. Could someone please fix this logic and little bit explain the situation.
var homeTeamPlayers: [MatchUp]? = []
let urlRequest = URLRequest(url: URL(string: "http://www.fibalivestats.com/data/586746/data.json")!)
func fetchHomeTeam() {
let task = URLSession.shared.dataTask(with: urlRequest) { (data,response,error) in
if error != nil {
print(error!)
return
}
homeTeamPlayers = [MatchUp]()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
if let teamsFromJSON = json["tm"] as? [String : AnyObject] {
if let homeTeam = teamsFromJSON["1"] as? [String : AnyObject] {
if let player = homeTeam["pl"] as? [String : AnyObject] {
for _ in player {
let homeTeamPlayer = MatchUp()
if let firstPlayer = player["1"] as? [String : AnyObject] {
if let name = firstPlayer["name"] as? String {
homeTeamPlayer.homeTeam_name = name
}
}
homeTeamPlayers?.append(homeTeamPlayer)
}
}
}
}
} catch let error {
print(error)
}
}
task.resume()
}
Here is the JSON I would like to fetch...
{
tm: {
1: {
pl: {
1: {
name: "R. Miniotas"
},
2: {
name: "T. Delininkaitis"
},
3: {
name: "V. Cizauskas"
},
4: {
name: "T. Klimavicius"
},
5: {
name: "V. Lipkevicius"
},
6: {
name: "M. LinkeviÄius"
},
7: {
name: "D. Seskus"
},
8: {
name: "T. Michnevicius"
},
9: {
name: "D. Gvezdauskas"
},
11: {
name: "A. Valeika"
}
}
You need to enumerate the dictionary using for (key, value) in syntax:
if let players = homeTeam["pl"] as? [String : Any] {
for (_, value) in players {
let homeTeamPlayer = MatchUp()
if let currentPlayer = value as? [String : Any],
let name = currentPlayer["name"] as? String {
homeTeamPlayer.homeTeam_name = name
}
homeTeamPlayers?.append(homeTeamPlayer)
}
}
However to avoid empty Matchup instances I'd recommend
if let players = homeTeam["pl"] as? [String : Any] {
for (_, value) in players {
if let currentPlayer = value as? [String : Any],
let name = currentPlayer["name"] as? String {
let homeTeamPlayer = MatchUp()
homeTeamPlayer.homeTeam_name = name
homeTeamPlayers?.append(homeTeamPlayer)
}
}
}
Note: The JSON dictionary in Swift 3 is [String:Any] and why is the homeTeamPlayers array optional?
And finally – as always - .mutableContainers is completely meaningless in Swift.
Your JSON data is a dictionary of dictionaries of dictionaries.
It looks like there is an outer key "tm" (teams) that then contains a dictionary with keys "1", "2", etc for each team, and then inside the teams, there are more dictionaries that again use the keys "1", "2", "3", etc for the players. This is not a great structure.
If you don't care about the order in which you fetch the items from your dictionaries then you can use the syntax:
for (key, value) in dictionary
...to loop through all the key/value pairs in a dictionary, but you do need to be aware that the order you get the key/value pairs is not defined. It might give you the entries in key order sometimes, and not in order other times.
If order is important then you would need to fetch the keys first, sort them, and then fetch the items (or some other technique). Getting the values sorted by key might look like this:
let keys = dictionary.keys.sorted($0 < $1)
for aKey in keys {
let aValue = dictionary[aKey]
//Do whatever you need to do with this entry
}
EDIT:
Your JSON data needed some editing to make it legal JSON:
{
"tm" : {
"1" : {
"pl" : {
"7" : {
"name" : "D. Seskus"
},
"3" : {
"name" : "V. Cizauskas"
},
"8" : {
"name" : "T. Michnevicius"
},
"4" : {
"name" : "T. Klimavicius"
},
"11" : {
"name" : "A. Valeika"
},
"9" : {
"name" : "D. Gvezdauskas"
},
"5" : {
"name" : "V. Lipkevicius"
},
"1" : {
"name" : "R. Miniotas"
},
"6" : {
"name" : "M. LinkeviÃÂius"
},
"2" : {
"name" : "T. Delininkaitis"
}
}
}
}
}
(All the keys and values had to be enclosed in quotes, and I needed to add closing braces to terminate the object graph)
I wrote code that took the above and read it into a JSON object, and then wrote it back out to JSON without whitespace. I then converted all the quotes to \".
Here is tested code that parses the JSON into objects, and then walks your data structure, with quite a bit of error checking:
let jsonString = "{\"tm\":{\"1\":{\"pl\":{\"7\":{\"name\":\"D. Seskus\"},\"3\":{\"name\":\"V. Cizauskas\"},\"8\":{\"name\":\"T. Michnevicius\"},\"4\":{\"name\":\"T. Klimavicius\"},\"11\":{\"name\":\"A. Valeika\"},\"9\":{\"name\":\"D. Gvezdauskas\"},\"5\":{\"name\":\"V. Lipkevicius\"},\"1\":{\"name\":\"R. Miniotas\"},\"6\":{\"name\":\"M. LinkeviÃius\"},\"2\":{\"name\":\"T. Delininkaitis\"}}}}}"
guard let data = jsonString.data(using: .utf8) else {
fatalError("Unable to convert string to Data")
}
var jsonObjectOptional: Any? = nil
do {
jsonObjectOptional = try JSONSerialization.jsonObject(with: data, options: [])
} catch {
print("reading JSON failed with error \(error)")
}
guard let jsonObject = jsonObjectOptional as? [String: Any] else {
fatalError("Unable to read JSON data")
}
//Fetch the value for the "tm" key in the outer dictionary
guard let teamsFromJSON = jsonObject["tm"] as? [String : Any] else {
fatalError("Can't fetch dictionary from JSON[\"tm\"]")
}
let teamKeys = teamsFromJSON
.keys //Fetch all the keys from the teamsFromJSON dictionary
.sorted{$0.compare($1, options: .numeric) == .orderedAscending} //Sort the key strings in numeric order
print("teamsFromJSON = \(teamsFromJSON)")
//loop through the (sorted) team keys in teamKeys
for aTeamKey in teamKeys {
guard let aTeam = teamsFromJSON[aTeamKey] as? [String: Any] else {
print("Unable to read array of teams")
continue
}
//Fetch the dictionary of players for this team
guard let playersDict = aTeam["pl"] as? [String: Any] else {
print("Unable to read players dictionary from team \(aTeamKey)")
continue
}
//Fetch a sorted list of player keys
let playerKeys = playersDict
.keys
.sorted{$0.compare($1, options: .numeric) == .orderedAscending}
print("playerKeys = \(playerKeys)")
//Loop through the sorted array of player keys
for aPlayerKey in playerKeys {
//Fetch the value for this player key
guard let aPlayer = playersDict[aPlayerKey] as? [String: String] else {
print("Unable to cast aPlayer to type [String: String]")
continue
}
//Attempt to read the "name" entry for this player.
guard let playerName = aPlayer["name"] else {
continue
}
//Deal with a player in this team
print("In team \(aTeamKey), player \(aPlayerKey) name = \(playerName)")
}
}
if let jsonData = try? JSONSerialization.data(withJSONObject: jsonObject, options: []),
let jsonString = String(data: jsonData, encoding: .ascii) {
print("\n\njsonString = \n\(jsonString)")
}
The output of that code is:
playerKeys = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "11"]
In team 1, player 1 name = R. Miniotas
In team 1, player 2 name = T. Delininkaitis
In team 1, player 3 name = V. Cizauskas
In team 1, player 4 name = T. Klimavicius
In team 1, player 5 name = V. Lipkevicius
In team 1, player 6 name = M. LinkeviÃius
In team 1, player 7 name = D. Seskus
In team 1, player 8 name = T. Michnevicius
In team 1, player 9 name = D. Gvezdauskas
In team 1, player 11 name = A. Valeika
(Your data is missing an entry for player 10.)
Suppose this is my URL:
URL: https://maps.googleapis.com/maps/api/directions/json?origin=19.0176147,72.8561644&destination=26.98228,75.77469&sensor=false
The URL mentioned above when entered in browser gives me a JSON response from google.
In this response, I have to fetch the value of distance and duration.
So my swift code to fetch the value is:
do{
let data = try Data(contentsOf: url!)
let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject]
if let routes = json?["routes"] as AnyObject? as? [[String: AnyObject]] {
//print(routes)
for r in routes {
if let legs = r["legs"] as? [[String: AnyObject]] {
for l in legs {
//Start Address
print("Start Address: \((l["start_address"]!) as Any)")
// End Address
print("End Address: \((l["end_address"]!) as Any)")
//Distance
if let distance = l["distance"] as? [String: AnyObject] {
distanceResult = (distance["text"]!) as Any as? String
print("Distance: \(distanceResult!)")
}
// Duration
if let duration = l["duration"] as? [String: AnyObject] {
durationResult = (duration["text"]!) as Any as? String
print("Duration: \(durationResult!)")
}
}
googleResult = distanceResult+durationResult
}
}
}
}
catch
{
distanceResult = ""
durationResult = ""
print("error in JSONSerialization")
}
if(googleResult != ""){
googleResult = "Distance: \(distanceResult!), Duration: \(durationResult!)"
}
else{
print("googleResult is nil")
distanceResult = ""
durationResult = ""
}
return googleResult

Swift not able to return the exact data from jsonResult

I'm trying to extract book data from the Google Books API:
https://www.googleapis.com/books/v1/volumes?q=isbn:9781451648546
I was able to get book title,description and thumbnail but I'm stuck on getting author and category exact informations (without the [" "]).
I'm getting this result with the previous link :
Author:["Walter Isaacson"]
Categories:[""Biography & Autobiography""]
"volumeInfo": {
"title": "Steve Jobs",
"authors": [
"Walter Isaacson"
],
"publisher": "Simon and Schuster",
"publishedDate": "2011",
and using this code on my iOS application:
if let arrayOfAuthors = (jsonResult as AnyObject).value(forKeyPath: "items.volumeInfo.authors") as? [[String]] {
DispatchQueue.global(qos: .userInitiated).async {
// Bounce back to the main thread to update the UI
DispatchQueue.main.async {
self.authorLabel.text = "Author: \(arrayOfAuthors[0])"
}
}
}
This worked for me
func parseJSON(){
let backgroundQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
backgroundQueue.async(execute: {
let serverResponseJSON:NSDictionary = self.parseURLToResponseJSON(urlToRequest: "https://www.googleapis.com/books/v1/volumes?q=isbn:9781451648546")
print(serverResponseJSON)
let arrayOfauthors:NSArray = serverResponseJSON.value(forKeyPath: "items.volumeInfo.authors") as! NSArray
print(arrayOfauthors)
DispatchQueue.main.async {
self.authorLabel.text = "Author: \(arrayOfAuthors[0])"
}
})
}
func parseURLToResponseJSON(urlToRequest: String) -> NSDictionary{
let inputData = try? Data(contentsOf: URL(string: urlToRequest)!)
let boardsDictionary: NSDictionary = (try! JSONSerialization.jsonObject(with: inputData!, options: JSONSerialization.ReadingOptions.mutableContainers)) as! NSDictionary
return boardsDictionary
}

Resources