How can I parse this JSON Pokemon Dictionary? (swift 3) - json

I have an issue with parsing JSON data from new version of the Pokemon API, specifically with values of the "defense" and the"attack".
In Pokemon API v1 it was easy...
//JSON:
"attack": 45,
"defense": 65
//After parsing in Alamofire i'm got the next solution:
if let dict = response.result.value as? Dictionary<String, AnyObject> {
if let attack = dict["attack"] as? Int {
self._attack = attack
}
if let defense = dict["defense"] as? Int {
self._defense = defense
}
print(self._attack)
print(self._defense)
In Pokemon API v2 i have an issue with JSON format:
//JSON:
"stats": [
{
"stat": {
"url": "http://pokeapi.co/api/v2/stat/3/",
"name": "defense"
},
"effort": 0,
"base_stat": 45
},
{
"stat": {
"url": "http://pokeapi.co/api/v2/stat/2/",
"name": "attack"
},
"effort": 0,
"base_stat": 65
}
]
I have tried this option, but it doesn't work:
if let stats = dict["stats"] as? [Dictionary<String, AnyObject>], stats.count > 0 {
if let stat = stats[0]["stat"] as? Dictionary<String, String>{
if name = stat["defense"] {
if let defense = stats[0]["base_stat"] as? Int {
self._defense = defense
}
}
}
}
print(self._defense)
Please advise, how can I parse and get the value of "defense" correctly?

The problem is in this line if name = stat["defense"] { your JSON has key name and defense & attack are its value, so you need to get its value and need to check is it defense or attack.
if name = stat["name"] as? String, name == "defense" {
}
You can also reduce the code of getting base_stat with single if let like this.
if name = stat["name"] as? String, let score = stats[0]["base_stat"] as? Int, name == "defense" {
print("defense : \(score)")
}

Try this
guard let statsNode = dict["stats"] as? [[String: Any]] else { return }
for (index, statNode) in statsNode.enumerated() {
guard let statValue = statNode["base_stat"] as? Int else { continue }
switch index {
case 0:
self._defense = statValue
case 1:
self._attack = statValue
case 2:
self._someStat = statValue
....
default:
break
}
}
print(self._attack)
print(self._defense)
I've worked on a similar project when I started out in iOS Development.
These days I prefer guard let statements over if let statements when parsing JSON for information that's required in my project
I'm also assuming since the stats node is an array, the order of attack, defense, special attack, special defense, speed and HP won't change so the switch statement is an appropriate tool to use in this case.
It might also be helpful to put a print statement before return and continue in the guard statements' else block to see if you hit those else blocks in the program.

Related

How do I access a specific value in this dictionary of JSON using Swift?

I am trying to get the total Wins from this API (Tracker Network API) and I have gotten the key and it displays the key and value like so.
The code is below and I am also able to get the number of wins (Integer) along with these values(Titles). However, I cannot figure out how to just get the "Wins" number without having all the other numbers printing out too.
I have tried
print(statsArray[8])
totalWins = statsArray[8]
//["value": 4350, "key": Wins]
print(totalWins.values)
//[Wins, 4350]
but it does not print it how I would like it to print. I would like it to print out as just the number so that I can then load that number into a UILabel.
What I am asking, is how do I print the "Wins" (integer) amount only and not the other 11 json outputs? I just want one of the numbers.
let epicName = "Ninja"
let formattedName = epicName.replacingOccurrences(of: " ", with: "%20")
let platform = "pc"
//pc, xbl, psn
let fortniteChallengesURL3 = URL(string: "https://api.fortnitetracker.com/v1/profile/\(platform)/\(formattedName)")
if let unwrappedURL = fortniteChallengesURL3 {
var request = URLRequest(url: unwrappedURL)
request.addValue("MyKey", forHTTPHeaderField: "TRN-Api-Key")
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data) as! [String:Any]
//print(json)
for (key, value) in json {
if (key == "lifeTimeStats") {
if let statsArray:[ [String : Any] ] = value as? [ [String : Any] ] {
//accessing the 8th but I am getting the output wrong
/*let firstKey = Array( (value as? [ [String : Any] ])!)[8]
*/
print(statsArray[8])
let totalWins = statsArray[8]
//["value": 4350, "key": Wins]
print(totalWins.values)
//[Wins, 4350]
for dict in statsArray {
for (key, value) in dict {
if (key == "key") {
//print ( "\(firstKey.values)")
print ( "keys are \(value)")
}
/*if (key == "value") {
print ( "value are \(value)")
}*/
}
}
}
}
}
} catch let error as NSError {
print(error.localizedDescription)
DispatchQueue.main.asyncAfter(deadline: .now() ) {
}
}
}
}
dataTask.resume()
}
TotalWins is a dictionary with two items and you can access any item value in a dictionary by its key, so try this:
print(totalWins["value"])
When you iterate over each item in the array of dictionaries you can access the key and the value fields directly like so:
for dic in statsArray {
let title = dic["key"]
let value = dic["value"] // You can use this as the value for the label
}
If you want to only access the total wins data, you could do:
let totalWinsValues = totalWins["value"]
You will want to look into creating a data model in the form of a struct of a class, and implementing the Codable protocol to make things easier and cleaner.

Read from API JSON using Alamofire Swift 4

i have hard time to read JSON using Alamofire. below is the structure of the API response
{
"data": [
{
"order": {
"id": 258,
"created_at": "2018-07-01T14:51:05+08:00",
"user_id": "1234"
},
"transactions": [
{
"transaction_type": "rent",
"cabinet_id": "02110A0000C6",
"jack_id": 1
}
]
}
]
}
Basically, i need to print out only the array of transactions and also print one by one the transaction_type, cabinet_id, and jack_id
The previous one i just manage to print the api response using code below
Alamofire.request(WalletRouter.urlUserActiveOrder(id: userId)).responseJSON { response in
if let value = response.result.value {
let dict = value as? Dictionary<String, AnyObject>
//print (value)
if let innerDict = dict!["data"] {
print (innerDict) //manage to return the value
let data = innerDict["transactions"] as! NSArray //get error here
print (data)
}
}
//do your json stuff
else if (response.result.isFailure) {
//Manager your error
switch (response.error!._code){
case NSURLErrorTimedOut:
//Manager your time out error
break
case NSURLErrorNotConnectedToInternet:
//Manager your not connected to internet error
break
default:
print ("error")
}
}
}
i have already spent 6 hour to solve this, but still failed to get the value.
Create a struct class like below
struct Result: Decodable {
struct data12: Decodable {
struct transaction: Decodable {
let transactionType:String
let cabinetId:String
let jackId:Int
}
let transactions:[transaction]
}
let data:[data12]
}
Data fetching like this
do {
let data1 = try Data(contentsOf: URL(fileURLWithPath: path), options: [])
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let courses = try decoder.decode(Result.self, from: data1)
print(courses)
} catch {
print(error)
}
if anyOne think my approach is wrong please update my answer TIA

Swift JSON values

I am new to iOS development and wanting some help here.
I have a JSON output from a webservice and I want to display the details in a custom table view cell. Actually, I am following a tutorial here: https://www.youtube.com/watch?v=ea6_a_zbQrY
In that tutorial, the JSON output is as follows:-
{
"actors": [
{
"name": "Brad Pitt",
"description": "William Bradley 'Brad' Pitt is an American actor and film producer. He has received a Golden Globe Award, a Screen Actors Guild Award, and three Academy Award nominations in acting categories",
"dob": "December 18, 1963",
"country": "United States",
"height": "1.80 m",
"spouse": "Jennifer Aniston",
"children": "Shiloh Nouvel Jolie-Pitt, Maddox Chivan Jolie-Pitt",
"image": "http://microblogging.wingnity.com/JSONParsingTutorial/brad.jpg"
},
{
"name": "Tom Cruise",
"description": "Tom Cruise, is an American film actor and producer. He has been nominated for three Academy Awards and has won three Golden Globe Awards. He started his career at age 19 in the 1981 film Endless Love.",
"dob": "July 3, 1962",
"country": "United States",
"height": "1.70 m",
"spouse": "Katie Holmes",
"children": "Suri Cruise, Isabella Jane Cruise, Connor Cruise",
"image": "http://microblogging.wingnity.com/JSONParsingTutorial/cruise.jpg"
},
{
"name": "Johnny Depp",
"description": "John Christopher 'Johnny' Depp II is an American actor, film producer, and musician. He has won the Golden Globe Award and Screen Actors Guild award for Best Actor.",
"dob": "June 9, 1963",
"country": "United States",
"height": "1.78 m",
"spouse": "Lori Anne Allison",
"children": "Lily-Rose Melody Depp, John 'Jack' Christopher Depp III",
"image": "http://microblogging.wingnity.com/JSONParsingTutorial/johnny.jpg"
},
My own JSON output are as follows:
[{"ID":"5662","Subject":"EXAM [JUNE 17 SEMESTER]","Course":"UNITAR","Lecturer":"EXAM OFFICER","CTime":"9:00AM-5:30PM","Venue":"10.03","TDate":"2017-09-04"},{"ID":"10314","Subject":"FAB","Course":"CAT","Lecturer":"DR CHONG","CTime":"9:00AM-12:00PM","Venue":"THEATRE ROOM 1 [LV 9]","TDate":"2017-09-04"},{"ID":"10317","Subject":"FMA","Course":"CAT","Lecturer":"GS ONG","CTime":"9:00AM-12:00PM","Venue":"9.09","TDate":"2017-09-04"},{"ID":"10318","Subject":"FFA","Course":"CAT","Lecturer":"MARGARET","CTime":"1:00PM-4:00PM","Venue":"THEATRE ROOM 1 [LV 9]","TDate":"2017-09-04"},{"ID":"10319","Subject":"MA1","Course":"CAT","Lecturer":"GS ONG","CTime":"1:00PM-4:00PM","Venue":"9.09","TDate":"2017-09-04"},{"ID":"10320","Subject":"P5","Course":"ACCA","Lecturer":"SPENCER","CTime":"6:15PM-9:45PM","Venue":"THEATRE ROOM 1 [LV 9]","TDate":"2017-09-04"},{"ID":"10324","Subject":"F8","Course":"ACCA","Lecturer":"MIKE KEE","CTime":"6:15PM-9:45PM","Venue":"9.02","TDate":"2017-09-04"},{"ID":"10325","Subject":"F2","Course":"ACCA","Lecturer":"GS ONG","CTime":"6:15PM-9:45PM","Venue":"9.09","TDate":"2017-09-04"},{"ID":"10326","Subject":"F4","Course":"ACCA","Lecturer":"HEMA","CTime":"6:15PM-9:45PM","Venue":"9.13","TDate":"2017-09-04"},{"ID":"11413","Subject":"M4","Course":"TG","Lecturer":"LAI WS","CTime":"7:00PM-10:00PM","Venue":"9.01","TDate":"2017-09-04"}]
Here is the code from the tutorial to parse the JSON values from the tutorial:
func downloadJsonWithURL() {
let url = NSURL(string: urlString)
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 {
print(jsonObj!.value(forKey: "actors"))
if let actorArray = jsonObj!.value(forKey: "actors") as? NSArray {
for actor in actorArray{
if let actorDict = actor as? NSDictionary {
if let name = actorDict.value(forKey: "name") {
self.nameArray.append(name as! String)
}
if let name = actorDict.value(forKey: "dob") {
self.dobArray.append(name as! String)
}
if let name = actorDict.value(forKey: "image") {
self.imgURLArray.append(name as! String)
}
}
}
}
OperationQueue.main.addOperation({
self.tableView.reloadData()
})
}
}).resume()
}
How do I modify this code as I don't have "actors" key in my JSON. Can someone guide me how to change this part?
This is one of the worst codes I've ever seen. Almost everything is wrong or a very bad programming habit.
The biggest mistakes are:
No error handling at all.
The usage of Foundation (NSArray / NSDictionary) rather than native collection types.
The usage of multiple string arrays rather than one custom struct / class as data model.
The forced unwrapping of the values rather than handling the optionals safely.
The usage of valueForKey rather than dedicated objectForKey or key subscription.
First of all create a struct as data model and one array as data source
struct Schedule {
let id, subject, course, lecturer, cTime, venue, tDate : String
}
var schedules = [Schedule]()
Assuming all values won't be changed the struct members are declared as constants (let). You get the memberwise initializer for free.
Reading JSON is very easy. There are only two collection types, array ([]) and dictionary ({}).
This JSON is an array of dictionaries ([{ .. }, { ...}]) . All keys and values are strings. The appropriate (native) Swift type is [[String:String]]. The code parses the JSON and assigns an empty string in case one of the keys does not exist.
func downloadJson(with urlString : String) {
guard let url = URL(string: urlString) else { print("bad URL"); return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let connectionError = error {
print(connectionError)
return
}
do {
if let scheduleArray = try JSONSerialization.jsonObject(with: data!) as? [[String:String]] {
for item in scheduleArray {
self.schedules.append(Schedule(id: item["ID"] ?? "",
subject: item["Subject"] ?? "",
course: item["Course"] ?? "",
lecturer: item["Lecturer"] ?? "",
cTime: item["CTime"] ?? "",
venue: item["Venue"] ?? "",
tDate: item["TDate"] ?? ""))
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
} catch {
print(error)
}
}
task.resume()
}
In the table view in cellForRow you can simply write
let schedule = schedules[indexPath.row]
aLabel.text = schedule.id
anotherLabel.text = schedule.subject
...
First ignore the JSON contents, and instead, think of it as an array of objects called as course.
[
{
"ID": "",
"Subject": "",
"Course": "",
"Lecturer": "",
"CTime": "",
"Venue": "",
"TDate": ""
},
...
]
So first you need to parse your JSON as an array. Let's call it as coursesArray.
if let coursesArray = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSArray
{
for course in CoursesArray
{
// course is a json object. So get it into a dictionary so that
// we can access the values in the course.
if let courseDict = course as? NSDictionary
{
// Now we can print the remaining properties of the course
let id = courseDict.value(forKey: "ID")
let subject = courseDict.value(forKey: "Subject")
let courseName = courseDict.value(forKey: "Course")
let lecturer = courseDict.value(forKey: "Lecturer")
let cTime = courseDict.value(forKey: "CTime")
let venue = courseDict.value(forKey: "Venue")
let tDate = courseDict.value(forKey: "TDate")
// Print them, or use them in any way you like now.
}
}
}
That should do about the extraction of the data. To be able to use these, you'll need to append them to other arrays, and reload the table. I'm leaving it to you.
Hope this helps.
Try using following code , take json object as [[String:Any]] and loop through all the present dictionaries in It to get all values you require
//Two arrays to store your data and use it as result
var IDs = [String]()
var Subjects = [String]()
//Your function
func downloadJsonWithURL() {
let url = URL(string: urlString)
URLSession.shared.dataTask(with: (url as? URL)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [[String:Any]] {
print(jsonObj)
//Loop through all the keys present in the dictionaries inside
for key in jsonObj{
//Need to provide required key to check and loop in it
let ID = key["ID"]
let Subject = key["Subject"]
//Append values in Arrays declared above
self.IDs.append(ID as! String)
self.Subjects.append(Subject as! String)
}
OperationQueue.main.addOperation({
self.tableView.reloadData()
})
}
}).resume()
}
In the tutorial the author's data format is dictionary of arrays and each array is of type dictionary so, in the code first author converted json data into NSDictionary then accessed array with actor keyword.
But all this steps are not necessary in your case, because your data is directly in an array of dictionaries. So first convert your data as NSArray and then convert each record of it as NSDictionary as shown in below code snippet.
func downloadJsonWithURL() {
let url = URL(string: urlString)
URLSession.shared.dataTask(with: (url as? URL)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [[String:String]] {
print(jsonObj)
for student in jsonObj{
if let studentDict = student as? [String:String] {
if let id = studentDict["ID"] ?? "" {
self.idArray.append(id)
}
if let subject = actorDict["Subject"] ?? "" {
self.subArray.append(subject)
}
}
}
OperationQueue.main.addOperation({
self.tableView.reloadData()
})
}
}).resume()
}

Can' read JSON element in Swift

I'm using Swift 3.0 and cannot seem to parse this JSON response.
{
"books": [{
"name": "NAME",
"key": "Key"
}],
"count": 1
}
Here is what I am using
let booksData = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
if let bookCount = booksData["count"] as? String {
print("found")
}
else {
print("Not Found")
}
I must be missing something really obvious here. I'm trying to read count before reading the array of books.
Try this:
if let bookCount = booksData["count"] as? NSNumber {
print("found")
} else {
print("Not Found")
}
In your JSON data, see "count": 1. The value 1 is a JSON number, which is converted to NSNumber with JSONSerialization. And as? casting from NSNumber to String always fails.
if let bookCount = bookData should be if let bookCount = booksData

How to insert JSON array model in Swift?

I have JSON like this but with more data:
[
{
"name": "Place 1",
"avatar": "https://sometext.it/image=1",
"id": "1",
"lng": 10.01,
"lat": 15.02
},
{
"name": "Place 2",
"avatar": "https://sometext.it/image=2",
"id": "2",
"lng": 15.02,
"lat": 15.03
}
]
I get JSON from URL and I want to insert them to array of places. I have class:
class Place {
var Avatar = ""
var Id = 0
var Lat = 0.0
var Lng = 0.0
var Name = ""
required init(avatar: String, id: Int, lat: Double, lng: Double, name: String) {
self.Avatar = avatar
self.Id = id
self.Lat = lat
self.Lng = lng
self.Name = name
}
}
And i create an Array:
var places: [Place] = []
I serialize JSON like this:
func parsingJson() {
guard let url = URL(string: "https://somelink.com") else {
return
}
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
for result in json as! [[String:Any]] {
let avatar = result["avatar"] as! String
let id = result["id"] as! Int
let lat = result["lat"] as! Double
let lng = result["lng"] as! Double
let name = result["name"] as! String
let place = Place(avatar: avatar, id: id, lat: lat, lng: lng, name: name)
self.places.append(place)
print(result)
}
}catch {
print("JSON Error")
}
}
}.resume()
}
but that doesn't work, I have error like this: http://obrazki.elektroda.pl/9267167800_1497627255.png
I know that I have nil but I don't know why :( When I print the JSON when I'm serialising I see it on console.
The first, you are trying to get ID as an INT, when your JSON Object is showing that it's a string.. So you would need to do:
var id = Int()
if let someID = result["id"] as? String {
id = Int(someId)
} else {
print("ID failed as String")
}
However, i'd also recommend using a guard statement before your for loop:
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [[String:Any]] else {
print("Invalid json object type")
return
}
for result in json {
///your code.
}
you at least try to unwrap the variable, and get some Int instance to pass along
But obviously, the simpler solution would be to save the id field in your JSON to the type you would want on your front-end.. in this case a Number.
While it might not look as clean as your code, use unwraping methods like guards, and if-else logics as much as possible, and create failed unwrap fallbacks as much as possible as early on in your project. It helps in debugging down the line and creates a good base when your project becomes large enough.
Check up on Apple's guide and this here for some good starting points.
Late update, but re-reading on all this landed me on this neat article by Apple:
https://developer.apple.com/swift/blog/?id=37
You haven't nil, read error - Could not cast value of type NSTaggedPointerString to NSNumber This means that your id is String and you are casting it to Int so replace
let id = result["id"] as! Int
to
let id = result["id"] as! String