Having difficulty parsing a response from an API call - ios

I'm trying to parse the response object from an API call, but having some difficulty.
First, I parsed the returned JSON with following:
let responseObject = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any]
if let result = responseObject["result"] {
print(result)
}
Then, when I log the result, following is what I get:
{
"_links" = {
next = "/api/3/action/datastore_search?limit=50&id=22f223e7-73f7-4842-935c-80a0ba5c3e5b&offset=50";
start = "/api/3/action/datastore_search?limit=50&id=22f223e7-73f7-4842-935c-80a0ba5c3e5b";
};
fields = (
{
id = "_id";
type = int;
},
{
id = package;
info = {
label = "";
notes = "Unique, normalized, name of dataset.
"type_override" = "";
};
type = text;
}
)
}
I tried parsing it again:
let finalResult = (try? JSONSerialization.jsonObject(with: result)) as? [String: Any]
But, I get the following error:
No exact matches in call to class method 'jsonObject'
Update
if let result = responseObject["result"] as? [String: Any] {
if let finalResult = result["records"] {
print(finalResult)
}
}
When I log this, I get the following:
(
{
"_id" = 186;
accessibility = 1;
completeness = "0.6899999999999999";
freshness = "0.5";
grade = Silver;
"grade_norm" = Silver;
metadata = "0.84";
package = "air-conditioned-and-cool-spaces-heat-relief-network";
"recorded_at" = "2019-12-17T20:24:09";
score = "0.78";
"score_norm" = "0.76";
usability = "0.86";
version = "v0.1.0";
},
{
"_id" = 187;
accessibility = 1;
completeness = 1;
freshness = 0;
grade = Bronze;
"grade_norm" = Bronze;
metadata = "0.25";
package = "air-conditioned-public-places-cooling-centres";
"recorded_at" = "2019-12-17T20:24:09";
score = "0.54";
"score_norm" = "0.31";
usability = "0.85";
version = "v0.1.0";
},
)
When I tried to iterate this:
for (key, value) in finalResult {
print("key", key)
print("value", value)
}
I get the following error:
Tuple pattern cannot match values of none-tuple type

Thanks to #OOPer, I was able to parse it as a dictionary:
if let result = responseObject["result"] as? [String: Any] {
if let finalResult = result["records"] as? [[String: Any]] {
finalResult.forEach { (catalogue) in
if let package = catalogue["package"] as? String {
}
}
}
}

Related

Get JSON value to Array

I need to get the JSON value from MYSQL and display it in the table view. I have done PHP part to receive data but I have error with my XCODE. My data will store temperature, humidity and digital status (on off) of sensor. I have questions below:
How to get ON/OFF JSON value and store in Bool array?
How to get JSON value and store in Float array?
if JSON value is , how can I store value as 0 in XCODE?
class DataManager {
var nodenameArray: [String] = []
var nodeidArray: [String] = []
var tempArray: [Float] = []
var humArray: [Float] = []
var pirArray: [Bool] = []
var lightArray: [Bool] = []
var relayArray: [Bool] = []
var hallArray: [Bool] = []
var smokeArray: [Bool] = []
#objc func taskdo() {
self.nodenameArray = []
self.nodeidArray = []
self.tempArray = []
self.humArray = []
self.pirArray = []
self.lightArray = []
self.relayArray = []
self.hallArray = []
self.smokeArray = []
if userlogin == true {
let request = NSMutableURLRequest(url: NSURL(string: http://talectric.com/wp-admin/a_p/iot/read_all.php")! as URL)
request.httpMethod = "POST"
let postString = "username=\(username)&password=\(password)&authen=wdwfesf9329140dsvfxkciospdkm"
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
if error != nil {
print("error=\(error!)")
return
} else {
do {
if let respondString = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
print(respondString)
if let nodedata = respondString.value(forKey: "nodedata") as? NSArray {
for node in nodedata{
if let nodeDict = node as? NSDictionary {
if let nodeid = nodeDict.value(forKey: "node_id") {
self.nodeidArray.insert(nodeid as! String, at: 0)
}
if let nodeid = nodeDict.value(forKey: "node_name") {
self.nodenameArray.insert(nodeid as! String, at: 0)
}
if let nodeid = nodeDict.value(forKey: "temp") {
self.tempArray.insert(Float(nodeid as! String)!, at: 0)
}
if let nodeid = nodeDict.value(forKey: "hum") {
print(nodeid)
self.humArray.insert(Float(Int(nodeid as! String)!), at: 0)
}
}
}
}
}
print(self.nodenameArray)
print(self.nodeidArray)
print(self.tempArray)
print(self.humArray)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
}
catch let error as NSError {
print(error.debugDescription)
}
}
}
task.resume()
}
}
}
below is the respond:
{
nodedata = (
{
"hall_status" = "<null>";
hum = 111;
"light_status" = "<null>";
"node_id" = y2cfwecrw3hqznuxmfvf;
"node_name" = SVIN03;
"pir_status" = OFF;
"relay_status" = "<null>";
"smoke_status" = "<null>";
temp = 2132;
},
{
"node_name" = SVIN04;
nodeid = aj2w1aljw8nd65ax79dm;
},
{
"hall_status" = "<null>";
hum = 100;
"light_status" = "<null>";
"node_id" = mwmfl2og2l8888fjpj2d;
"node_name" = SVIN05;
"pir_status" = ON;
"relay_status" = "<null>";
"smoke_status" = "<null>";
temp = 45;
}
);
numberofnodeid = 3;
}
111
100
Could not cast value of type '__NSCFNumber' (0x10db6f878) to 'NSString' (0x10ca71568).
2018-10-11 08:11:05.352491+0700 IOT 2[1506:101847] Could not cast value of type '__NSCFNumber' (0x10db6f878) to 'NSString' (0x10ca71568).
error is in this line :
self.humArray.insert(Float(Int(nodeid as! String)!), at: 0)
First of all, you have to look at json_decode to read data from json in php
Example:
`$json = '{"foo-bar": 12345}';
$obj = json_decode($json);
print $obj->{'foo-bar'}; // this will print 12345`
How to get ON/OFF JSON value and store in Bool array ?
basically you have to add a new value to your onOff variable, something like
$onOff=array();
$val;
$obj = json_decode($json);
if (is_null($obj->{'name_of_the_alert'})) {
$val = 0
} else {
$val = $obj->{'name_of_the_alert'};
}
array_push($onOff,$obj->{'name_of_the_alert'});
How to get JSON value and store in Float array ?
Basically same thing, I haven't use PHP in a while, but just declare the array of the value type you need.
if JSON value is , how can i store value as 0 in XCODE ?
I guess you mean if it is null, in this case you have to do this
$onOff=array();
$val;
$obj = json_decode($json);
if (is_null($obj->{'name_of_the_alert'})) {
$val = 0
} else {
$val = $obj->{'name_of_the_alert'};
}
array_push($onOff,$obj->{'name_of_the_alert'});
Take into consideration that I may have some mistakes here because I am not familiar with newer PHP versions.
Good luck
From your question, it is clear that temp and hum are Number type(Int of Double). So use NSNumber rather than NSString as:
self.humArray.insert(Float(Int(nodeid as! NSNumber)!), at: 0)
As more swifty approach, you can update your code as:
if let nodeDict = node as? [String: Any] {
if let nodeid = nodeDict["node_id"] as? String {
self.nodeidArray.insert(nodeid, at: 0)
}
if let nodename = nodeDict["node_name"] as? String {
self.nodenameArray.insert(nodename, at: 0)
}
if let temp = nodeDict["temp"] as? Int {
self.tempArray.insert(Float(temp), at: 0)
}
if let hum = nodeDict["hum"] as? Int {
self.humArray.insert(Float(hum), at: 0)
}
}
There is plenty of issues with your code:
First, avoid using NSStuff in Swift3+ when there is the equivalent.
let request = NSMutableURLRequest(url: NSURL(string: http://talectric.com/wp-admin/a_p/iot/read_all.php")! as URL)
...
let task = URLSession.shared.dataTask(with: request as URLRequest) {
=>
let request = URLRequest(url: URL(string: http://talectric.com/wp-admin/a_p/iot/read_all.php")!)
...
let task = URLSession.shared.dataTask(with: request) {
See, you avoid already all the as URL, as URLRequest.
Avoid force unwrapping: Avoid using !.
Because if it's nil or with as! the cast fails (and then it's nil), you'll get a crash.
Use if let, guard let. Read this about Optional
As a sample, I would have wrote:
guard let url = URL(string:http...) else {
print("invalid URL")
return
}
let request = URLRequest(url: url)
Parsing part:
if let respondString = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
Get rid of NSDictionary, use [String: Any] instead, then get rid of .allowFragments, it's rarely useful (it makes sense on certains cases only) don't use the force unwrap on data!, and don't name your variable respondString
because that's clearly not a String and that's clearly misleading.
=>
guard let data = data else { return }
if let responseDict = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
Now:
if let nodedata = respondString.value(forKey: "nodedata") as? NSArray {
for node in nodedata{
if let nodeDict = node as? NSDictionary {
if let nodeid = nodeDict.value(forKey: "node_id") {
self.nodeidArray.insert(nodeid as! String, at: 0)
}
if let nodeid = nodeDict.value(forKey: "node_name") {
self.nodenameArray.insert(nodeid as! String, at: 0)
}
if let nodeid = nodeDict.value(forKey: "temp") {
self.tempArray.insert(Float(nodeid as! String)!, at: 0)
}
if let nodeid = nodeDict.value(forKey: "hum") {
print(nodeid)
self.humArray.insert(Float(Int(nodeid as! String)!), at: 0)
}
}
}
}
No, no NSArray, no NSDictionary, avoid using value(forKey:) because it does what you think it does on a NSDictionary doing a object(forKey:), or simply subscript (using ["someKey"]) but on an Array you'll get an issue with an unexpected result I think.
Don't name all your var nodeid, it's harder to debug after ward, don't do a soft unwrap (if let) if just afterward you do a force unwrap on that value.
if let nodedata = responseDict["nodedata"] as? [[String: Any]] {
for aNode in nodedata {
if let id = aNode["node_id"] as? String {
self.nodeidArray.insert(nodeId, at: 0)
}
if let name = aNode["node_name"] as? String {
self.nodenameArray.insert(name, at: 0)
}
if let temp = aNode["temp"] as? String, let tempFloat = Float(temp) {
self.tempArray.insert(tempFloat, at: 0)
}
if let hum = aNode["hum"] as? String, let humFloat = Float(hum) {
self.humArray.insert(humFloat, at: 0)
}
}
}
That's much more readable.
Now instead of using insert(_:at:), it might be easier to use nodedata.inverted().
Now, let's talk about architecture:
What happens in your case, if you don't pass the if let hum = aNode["hum"] as? String, let humFloat = Float(hum) { on one iteration (let's say the second one which is exactly your case in the sample you gave there is no temp) but success on all the others?
Here what's going to happen:
self.nodeidArray[3] and self.humArray[3] won't be correct, they'll be desynchronized, right?
Because self.nodeidArray[2] should be with self.humArray[1].
Theses values are meant to be synce'd, so use a custom Struct/Class for it:
After all, Swift is a OOP (Object Oriented Programming) language, so use objects.
struct Node {
let id: String?
let name: String?
let humidity: Float?
let temperature: Float?
}
So instead of having:
var nodenameArray: [String] = []
var nodeidArray: [String] = []
var tempArray: [Float] = []
var humArray: [Float] = []
Just have:
var nodes: [Node] = []
And during the parsing, do
let node = Node.init(withDict: aNode)
nodes.append(node) //or insert it at 0
You can construct your own method init in Node, let's say:
func init(withDict dict: [String: Any]) {
if let id = dict["node_id"] as? String {
self.id = id
}
etc.
}
Having this Node class simplify. But using the JSONSerialization and init all the values manually could be avoided using Codable (available in Swift 4).
Side Note:
This code is not tested, there might be some glitch, typo etc. Let's focus on the explanation instead.
Lastly concerning the error:
Could not cast value of type '__NSCFNumber' (0x10db6f878) to
'NSString' (0x10ca71568). 2018-10-11 08:11:05.352491+0700 IOT
2[1506:101847] Could not cast value of type '__NSCFNumber'
(0x10db6f878) to 'NSString' (0x10ca71568).
error is in this line :
self.humArray.insert(Float(Int(nodeid as! String)!), at: 0)
The error means that at some point you tried to cas (using as) on a a (NS)String, but is was in fact NSNumber (in Swift, it's easily converted into an Int/Float, etc.), so it failed.
So the part that failed: nodeid as! String, because nodeid is not a (NS)String

How to get JSON object from another JSON object in swift 3

how can I get applicationStateJson in below code, I can parse the bellow JSON object from data but can not able to get applicationStateJson from it
{
applicationStateJson = {
"address_status" = 0;
email = "xxxxxxxx;
"email_status" = 0;
"first_name" = xxx;
"last_name" = "";
"login_status" = 1;
loginstype = "<null>";
mobile = xxxxxx;
"mobile_status" = 1;
notifications = (
);
"notifications_size" = 0;
"otp_status" = 3;
"profile_id" = "<null>";
"profile_name" = "virtual_recruiter";
"user_id" = 454;
"user_status" = 1;
"virtual_recruiter_id" =xxx;
};
"error_msg" = "<null>";
status = "<null>";
}
code as
do {
let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(json)
let applicationStateJson = try json["applicationStateJson"]
} catch { print(error.localizedDescription) }
Try with below code
First you need to change in below line, its Dictionary<String, Any> not an AnyObject
let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as! Dictionary<String, Any>
And then get applicationStateJson by below code
if let appJson = json["applicationStateJson"] as? Dictionary<String, Any> {
print(appJson)
print("Email : \(appJson["email"] as? String ?? "Email not found")") // For get email
}
The root object of the JSON is a clearly a dictionary, not AnyObject (an object but I-have-no-idea-what-it-is).
The value for key applicationStateJson is a dictionary, too.
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String:Any],
let applicationStateJson = json["applicationStateJson"] as? [String:Any] {
print(applicationStateJson)
}
} catch {
print(error)
}
As always, .mutableContainers is completely meaningless in Swift

How do I pluck this specific node out of from this JSON array?

I have an array being returned in Xcode which outputs the following:
["userDetails": {
id = 31;
"user_email" = "steve#gmail.com";
"user_name" = "Steve Downs";
}, "communities": <__NSArrayI 0x600000245f40>(
{
id = 5;
name = South;
},
{
id = 13;
name = HurraHarry;
},
{
id = 15;
name = EnclliffeT;
}
)
]
The code below correctly assigns the values contained within "communities" to their respective variables.
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:AnyObject]
print (json!)
if let arr = json?["communities"] as? [[String:String]] {
self.communitiesArray = arr.flatMap { $0["name"]!}
self.communityIdsArray = arr.flatMap { $0["id"]!}
}
if let arr = json?["userDetails"] as? [AnyObject] {
self.playerId = (arr.flatMap { $0["id"]!} as (AnyObject)) as! [String]
print (self.playerId);
}
However I'm having trouble assigning id from "userDetails". How do I take id from "userDetails" and assign it to self.playerId?
You can try something like this
if let dict = json?["userDetails"] as? [String:String] {
self.playerId = dict["id"]
}
Well the correct would be I believe:
guard let item = json?["userDetails"] as? [String: AnyObject],
self.playerId = item["id"] as? Int else {
return;
}

Accessing JSON Objects parsed from iTunes API in Swift 3.0

I am trying to access items parsed in JSON from the iTunes API using Swift 3.0, but I am struggling to access the objects after they have been parsed. The objects are being parsed in this format:
{
resultCount = 50;
results = (
{
artistId = 70936;
artistName = "Johnny Cash";
artistViewUrl = "https://itunes.apple.com/us/artist/johnny-cash/id70936?uo=4";
artworkUrl100 = "http://is2.mzstatic.com/image/thumb/Music3/v4/13/ae/73/13ae735e-33d0-1480-f51b-4150d4a45696/source/100x100bb.jpg";
artworkUrl30 = "http://is2.mzstatic.com/image/thumb/Music3/v4/13/ae/73/13ae735e-33d0-1480-f51b-4150d4a45696/source/30x30bb.jpg";
artworkUrl60 = "http://is2.mzstatic.com/image/thumb/Music3/v4/13/ae/73/13ae735e-33d0-1480-f51b-4150d4a45696/source/60x60bb.jpg";
collectionCensoredName = "The Essential Johnny Cash";
collectionExplicitness = notExplicit;
collectionId = 251001680;
collectionName = "The Essential Johnny Cash";
collectionPrice = "14.99";
collectionViewUrl = "https://itunes.apple.com/us/album/ring-of-fire/id251001680?i=251002253&uo=4";
country = USA;
currency = USD;
discCount = 2;
discNumber = 1;
isStreamable = 1;
kind = song;
previewUrl = "http://a1144.phobos.apple.com/us/r1000/070/Music/b3/99/be/mzi.qvkhtgfg.aac.p.m4a";
primaryGenreName = Country;
releaseDate = "2002-02-12T08:00:00Z";
trackCensoredName = "Ring of Fire";
trackCount = 18;
trackExplicitness = notExplicit;
trackId = 251002253;
trackName = "Ring of Fire";
trackNumber = 15;
trackPrice = "1.29";
trackTimeMillis = 155707;
trackViewUrl = "https://itunes.apple.com/us/album/ring-of-fire/id251001680?i=251002253&uo=4";
wrapperType = track;
},
I want to be able to access the information from all 50 results, such as the artistName, for instance. This is my parsing function attempting to get the artistName and add it to my NSDictionary, but it keeps returning that it can't unwrap the dictionary.
func parser() {
let enteredText:String = (tbxSearch.text?.replacingOccurrences(of: " ", with: "+"))!
let url = "https://itunes.apple.com/search?term=\(enteredText)"
print(url)
guard let urlRequest = URL(string: url) else
{
print("Error creating endpoint")
return
}
let request = URLRequest(url: urlRequest)
URLSession.shared.dataTask(with: request) {(data,response,error) in
do
{
guard let data = data else
{
return
}
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? NSDictionary else
{
return
}
if let results = json["results"] as? NSDictionary
{
self.avObjects.avDict.setValue("Artist Name", forKey: results["artistName"] as! String)
print(self.avObjects.avDict)
}
else
{
print("Couldn't unwrap the dictionary.")
}
print(json)
}
catch let error as NSError
{
print(error.debugDescription)
}
}.resume()
}
It looks like results is an array of dictionaries, not just a dictionary.
Instead of this: if let results = json["results"] as? NSDictionary
try this: if let results = json["results"] as? NSArray
You could then map or iterate over each element in the array, extracting "artistName" from each one, for example.
results is an array of dictionaries, not a dictionary itself. Try changing this:
if let results = json["results"] as? NSDictionary
to:
if let results = json["results"] as? NSArray
and then iterating over results. Each element of results is a dictionary with attributes such as artistName.

How to handle json error in loop or stop it

I have the following code that populates a UITableView. The end var holds the number of items in the JSON response. I concatenate variable n with the counter i. My problem is that in this case the JSON response carries only two items, Request1 and Request2. When the counter reaches 3 the app crashes because there is no Request3. How can I change my loop to stop when the condition counter > end is met?
let jsonData = try NSJSONSerialization.JSONObjectWithData(urlData!, options: .MutableContainers) as? NSDictionary
var end = jsonData!["num"]!
var i = 0
var n = "Request"
for item in jsonData! {
i++
n = "Request"+String(i)
var result = jsonData![n] as? NSDictionary
if let Name = result!["Name"] as? String
{
Names.append(Name)
print(Name)
}
if let Date = result!["Request_Id"] as? String
{
Dates.append(Date)
print(Date)
}
}
Try the following:
let jsonData = try NSJSONSerialization.JSONObjectWithData(urlData!, options: .MutableContainers) as? NSDictionary
var end = jsonData!["num"]!
var i = 0
var n = "Request"
for item in jsonData! {
i++
// This should do it
if i == end {
break;
}
n = "Request"+String(i)
var result = jsonData![n] as? NSDictionary
if let Name = result!["Name"] as? String
{
Names.append(Name)
print(Name)
}
if let Date = result!["Request_Id"] as? String
{
Dates.append(Date)
print(Date)
}
}
I'm assuming that by counter > end you mean i > end, as it's what seems to make sense to me.
Try starting your counter i with 1 rather than 0 incrementing it at the end of the loop like this:
let jsonData = try NSJSONSerialization.JSONObjectWithData(urlData!, options: .MutableContainers) as? NSDictionary
var end = jsonData!["num"]!
var i = 0
var n = "Request"
for item in jsonData! {
i++;
if(i==end)break;
n = "Request"+String(i)
var result = jsonData![n] as? NSDictionary
if let Name = result!["Name"] as? String
{
Names.append(Name)
print(Name)
}
if let Date = result!["Request_Id"] as? String
{
Dates.append(Date)
print(Date)
}
}
Also you can consider the following way of doing it
let jsonData = try NSJSONSerialization.JSONObjectWithData(urlData!, options: .MutableContainers) as? NSDictionary
var end = jsonData!["num"]!
var i = 0
var n = "Request"
while(i<end) { //since we are iterating on the basis on the number and specific hardcoded keys of jsondata (and not item) we can just use a normal while loop
i++;
n = "Request"+String(i)
var result = jsonData![n] as? NSDictionary
if let Name = result!["Name"] as? String
{
Names.append(Name)
print(Name)
}
if let Date = result!["Request_Id"] as? String
{
Dates.append(Date)
print(Date)
}
}

Resources