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.
Related
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
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
Hi I'm trying to get data from a certain JSON API. I can gat a snapshot of all values from the API, which is shown below. But I can't manage to put a specifiek row in a variable. This is the JSON form which I get. I want to print the "Description" value.Can someone help me with this?
And Hier is my code:
func apiRequest() {
let config = URLSessionConfiguration.default
let username = "F44C3FC2-91AF-5FB2-8B3F-70397C0D447D"
let password = "G23#rE9t1#"
let loginString = String(format: "%#:%#", username, password)
let userPasswordData = loginString.data(using: String.Encoding.utf8)
let base64EncodedCredential = userPasswordData?.base64EncodedString()
let authString = "Basic " + (base64EncodedCredential)!
print(authString)
config.httpAdditionalHeaders = ["Authorization" : authString]
let session = URLSession(configuration: config)
var running = false
let url = NSURL(string: "https://start.jamespro.nl/v4/api/json/projects/?limit=10")
let task = session.dataTask(with: url! as URL) {
( data, response, error) in
if let taskHeader = response as? HTTPURLResponse {
print(taskHeader.statusCode)
}
if error != nil {
print("There is an error!!!")
print(error)
} else {
if let content = data {
do {
let array = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(array)
if let items = array["items"] {
if let description = items["Description"] as? [[String:Any]]{
print(description as Any)
}
}
}
catch {
print("Error: Could not get any data")
}
}
}
running = false
}
running = true
task.resume()
while running {
print("waiting...")
sleep(1)
}
}
First of all the array is not an array and not AnyObject, it's a dictionary which is [String:Any] in Swift 3.
let dictionary = try JSONSerialization.jsonObject(with: content) as! [String:Any]
print(dictionary)
I don't know why all tutorials suggest .mutableContainers as option. That might be useful in Objective-C but is completely meaningless in Swift. Omit the parameter.
The object for key itemsis an array of dictionaries (again, the unspecified JSON type in Swift 3 is Any). Use a repeat loop to get all description values and you have to downcast all values of a dictionary from Any to the expected type.
if let items = dictionary["items"] as? [[String:Any]] {
for item in items {
if let description = item["Description"] as? String {
print(description)
}
}
}
Looks like items is an array that needs to be looped through. Here is some sample code, but I want to warn you that this code is not tested for your data.
if let items = array["items"] as? [[String: AnyObject]] {
for item in items {
if let description = item["Description"] as? String{
print("Description: \(description)")
}
}
}
This code above, or some variation of it, should get you on the right track.
use the SwiftyJSON and it would be as easy as json["items"][i].arrayValue as return and array with items Values or json["items"][i]["description"].stringValue to get a string from a row
I am new on swift and I am getting a json back from a request but I can not parse. I am trying to get the json info and create coordinates to use on mapkit with annotations as well
Below is the json I get back
{
coord = [
{
islocationactive = 1;
latitude = "37.8037522";
locationid = 1;
locationsubtitle = Danville;
locationtitle = "Schreiner's Home";
longitude = "121.9871216";
},
{
islocationactive = 1;
latitude = "37.8191921";
locationid = 2;
locationsubtitle = "Elementary School";
locationtitle = Montair;
longitude = "-122.0071005";
},
{
islocationactive = 1;
latitude = "37.8186077";
locationid = 3;
locationsubtitle = "Americas Eats";
locationtitle = "Chaus Restaurant";
longitude = "-121.999046";
},
{
islocationactive = 1;
latitude = "37.7789669";
locationid = 4;
locationsubtitle = "Cheer & Dance";
locationtitle = Valley;
longitude = "-121.9829908";
}
] }
and my code to try to parse is this
let task = URLSession.shared.dataTask(with: request as URLRequest){
data, response, error in
//exiting if there is some error
if error != nil{
print("error is \(error)")
return;
}
//parsing the response
do {
//converting resonse to NSDictionary
var teamJSON: NSDictionary!
teamJSON = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
print(teamJSON)
//getting the JSON array teams from the response
let liquidLocations: NSArray = teamJSON["coord"] as! NSArray
//looping through all the json objects in the array teams
for i in 0 ..< liquidLocations.count{
//getting the data at each index
// let teamId:Int = liquidLocations[i]["locationid"] as! Int!
}
} catch {
print(error)
}
}
//executing the task
task.resume()
but not that I try works. I want to get the latitude, longitude and create an annotationn on the map
Thanks for the help
You can try with below code its same as #Niko Adrianus Yuwono but made some changes so you will get teamid as integer
do {
let data : NSData = NSData() // change your data variable as you get from webservice response
guard let teamJSON = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: Any],
let liquidLocations = teamJSON["coord"] as? [[String: Any]]
else { return }
//looping through all the json objects in the array teams
for i in 0 ..< liquidLocations.count{
let teamId: Int = (liquidLocations[i]["locationid"] as! NSString).integerValue
print(teamId)
}
} catch {
print(error)
}
Try this
do {
guard let teamJSON = try JSONSerialization.jsonObject(with: data!, options: []) as? [String: Any],
let liquidLocations = teamJSON["coord"] as? [[String: Any]]
else { return }
//looping through all the json objects in the array teams
for i in 0 ..< liquidLocations.count{
let teamId: Int = (liquidLocations[i]["locationid"] as! NSString).integerValue
}
} catch {
print(error)
}
The key is not to use NSDictionary and NSArray because it's not strongly-typed (Although you can make it strongly-typed too) use Swift's array and Dictionary where you can use [Element Type] for array and [Key: Value] for dictionary
let urlAsString = "https://drive.google.com/uc?export=download&id=0B2bvUUCDODywWTV2Q2IwVjFaLW8"
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
do {
if let jsonDate = data, let jsonResult = try NSJSONSerialization.JSONObjectWithData(jsonDate, options: []) as? NSDictionary {
print(jsonResult)
}
} catch let error as NSError {
print(error)
}
})
jsonQuery.resume()
Okay so here i am receiving data from online json then storing it as NSDictionary in jsonresult . I need to get all keys and values as into two separate arrays ?
Basically i want this
jsonresult.allkeys --> String array
jsonresult.allvalues --> String array
You can use:
let keys = jsonResult.flatMap(){ $0.0 as? String }
let values = jsonResult.flatMap(){ $0.1 }
It is quite simple because you are using jsonResult as NSDictionary.
let dict: NSDictionary = ["Key1" : "Value1", "Key2" : "Value2"]
let keys = dict.allKeys
let values = dict.allValues
In you're case
let keys:[String] = dict.allKeys as! [String]
var values:[String]
if let valuesSting = dict.allValues as? [String] {
values = valuesSting
}
For anyone trying it with newer version Swift please use compactMap()instead of flatMap()
let keys = jsonResult.compactMap(){ $0.0 as? String }
let values = jsonResult.compactMap(){ $0.1 }