Parse Dictionary of Array of Dictionary in Swift 3 - ios

I am working on a project where I am getting a dictionary of an array which again containt the dictionary
My Json Response is
{
"code": 200,
"status": "OK",
"success": "true",
"message": "success",
"data": {
"vehicletypeData": [
{
"vehicle_type_id": "1",
"vehicle_type": "Any"
},
{
"vehicle_type_id": "11",
"vehicle_type": "Bike"
}
]
}
}
And I am parsing the data like
if response.success {
let resObj = response.responseObject as! Dictionary<String, Any>
let catArray = resObj["data"] as! Dictionary<String,Array<Dictionary<String,Any>>> // Crashes here
let vehicleData = catArray["vehicletypeData"] as! Array<Dictionary<String, Any>>
for vehicle in vehicleData {
self.jobCategories.append(PreJobVehicleData.mj_object(withKeyValues: vehicle))
}
}
I am trying to parse it in my model
Here I am getting an error like
- Could not cast value of type '__NSArrayM' (0x109b82e00) to 'NSDictionary' (0x109b832d8)
Any help will be Thankful.

Don't say
resObj["data"] as! Dictionary<String,Array<Dictionary<String,Any>>>
That's too specific. Just say
resObj["data"] as! [String:Any]
You know that when you get something by key out of that dictionary, it will be an array, but you can literally cross that bridge when you come to it.
The same rule applies to your other casts. Just cast to Swift dictionary or array using the broadest simplest possible type.
(Note that all this will be solved in Swift 4, where you can build a knowledge of the JSON structure right into your fetch.)

As you are parsing the nodes separately anyway avoid to cast to (more than two levels) nested types.
According to the error message the value for key data seems to be an array (Array<Dictionary<String, Any>>)
if response.success {
let resObj = response.responseObject as! Dictionary<String, Any>
let dataArray = resObj["data"] as! Array<Dictionary<String, Any>>
...
That implies however that the JSON output is wrong and I have no idea what's next...

Related

Unable to Parse a JSON Object iOS Swift

When I try to Parse a JSON object,
let json = try? JSONSerialization.jsonObject(with: reportData, options: []) as? [String : Any]
I get an error saying that
Cannot invoke 'jsonObject' with an argument list of type
'with:([String:Any]), options:[Any]'
Here is my reportData in JSON format, obtained from Server
{
"status": "success",
"statusCode": 200,
"message": "Report exists",
"patientReport": {
"caseId": "case040784",
"Observations": "These are test observations",
"userUid": "MY5FDbl0bgZStAY5Ky6OtYAzbDT2",
"nextSteps": "Here are my next steps",
"customerUid": "customerUid",
"results": {
"test1": "12",
"test3": "15",
"test3": "12"
}
}
}
Could someone please guide me where I am going wrong.
This function:
jsonObject(with: Data, options: JSONSerialization.ReadingOptions = [])
requires two parameters with type Data and JSONSerialization.ReadingOptions.
Your reportData is Dictionary not a Data. So you can use it without parse to Dictionary.
Cannot invoke 'jsonObject' with an argument list of type 'with:([String:Any]), options:[Any]'
I think this may be the strangest thing I've seen today.
Reading this message literally says that reportData is of type ([String: Any])—A tuple containing a single value [String: Any].
After reading Creating Tuples For a Single Value, I see there is no difference between ([String: Any]) and [String: Any].
The other users have already answered it… You don't need to parse the already dictionary object, I just found the tuple thing intresting.

Parsing json data using swift json

I can’t get the JSON data of message using swiftyjson.
When i print JSON value is there. But, when i print(json["result"]["message"]) it is null
{
"result": [{
"message": "success",
"age": "25"
}]
}
let json = JSON(data:jdata)
print(json)
print(json["result"]["message"])
json["result"] seems to be an array, you have to cast it to array like
let array = json["result"].arrayValue
let message = array[0]["message"]
You result is of array type. And you have to set index of object.
Try:
var array = json["result"].arrayValue
print(array[0]["message"])
You can also check this question
Hope it helps
Try:
let json = JSON(data: jdata)
let message = json["result"].array?.first?["message"]
print(message)

Can't cast from Dictionary value to Array

No idea why this won't work. The value of this dictionary is a key, yet whenever I try to cast to an array, I get a multitude of errors. Here's what I've tried, with the error I get following each example (in all examples, parameters is type [String : Any]:
let paramsArray = parameters["inputVO"] as AnyObject
if let array = paramsArray as? Array {
}
Error: Generic parameter 'Element' could not be inferred in cast to 'Array<_>'
if let array = parameters["inputVO"] as? Array {
}
Error: Ambiguous reference to member 'subscript'
I'm not sure what else to do to cast the result to an array? I'm sure I've done this before, I have no idea why this is failing. Any help is greatly appreciated.
Edit: Here's the output when I print out params. As expected, it is populated with an Array of Dictionary's.
Optional([["stmtDate": cmd, "transId": identifier, "isSupplementDataAvailable": true]])
Either it's an array
if let array = parameters["inputVO"] as? [[String:Any]] { ... }
or a dictionary
if let dictionary = parameters["inputVO"] as? [String:Any] { ... }
Both types are generics and need specific type information
[[String:Any]] is the short form of Array<Dictionary<String,Any>>
[String:Any] is the short form of Dictionary<String,Any>

When parsing JSON data from web service, no data returned

I'm trying to populate a UITableView from the results from a call to a web service. The data is returned in JSON format as below
[{"name":"CONTROL TEST",
"nc_lead":"TOM SMITH",
"datastr":"N/A",
"username":"tsmith",
"status":"REOPENED",
"orderno":"00000000",
"ccsText":"000",
"risk":0,
"dateTimeCreated":"2016-01-29 13:16:50",
"supplyStart":"2016-02-08T11:51:00+0000",
"supplyEnd":"2016-02-08T12:03:00+0000",
"logs":
[{"dateTime":"2016-02-02 11:26:18",
"statusChangeDateTime":"2016-02-02 00:00:00",
"user":"tsmith",
"uid":1,
"task":"TESTING",
"info":"state changed to 'Y'",
"x":456123,
"y":362514,
"id_log":28294},
{“dateTime":"2016-02-02 10:54:34",
"statusChangeDateTime":"2016-02-02 10:54:00",
"user":"tsmith",
"uid":1,
"task":"TESTING",
"info":"T Smith changed area.",
"x":452356,
"y":325489,
"id_log":28291
},.....
There is no key as the first part is formed by 12 values as a header and then the logs form an array of values for each header. I return my JSON object using the code below, parsing the data object from my web service call.
let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
I can see the return value for data using the code below
let str = NSString(data: data!, encoding: NSUTF8StringEncoding)
then I've tried using code such as the code below to extract a value for 'name' but it doesn't work
if let name = json["name"] as? [[String: AnyObject]] {}
How can I iterate through my json object to get the data from the header and the logs array?
It seems json object is an array of dictionary, so you should do something like this:
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments) as? Array<Dictionary<String, AnyObject>> {
for item in json {
if let dict = item as? Dictionary<String, AnyObject> {
print(dict["name"])
}
}
}
The conversion should be to AnyObject because sometimes the value is String, like for example dict["name"] == "CONTROL TEST", but dict["logs"] is Array<Dictionary<String, String>>. Do you see it?

Optional Values in NSMutableDictionary

I'm new to Swift, very experienced in Objective-C.
In my app, I am receiving data from a server and mapping it to an NSMutableDictionary. The reason I am using an NSMutableDictionary is that the values are not consistent coming from the server, it's a mix of strings and numbers. And that appears to break a Swift Dictionary that expects only one type of value.
Sometimes the server is not sending a value that the NSMutableDictionary is expecting and it crashes the app.
In my research, it appears that I have to check every object to see if the value exists in Swift before setting it into the NSMutableDictionary.
This is my current code:
let userDictionary:NSMutableDictionary = [
"name": data.objectForKey("name") as! String,
... // many more values
This crashes if there is no "name" value in the response from the server.
It appears the solution would be:
if let nameValue = data.objectForKey("name") as! String {
//not sure what to do in here since the var is assigned as I need
}
// ... check many more values
let userDictionary:NSMutableDictionary = [
"name": nameValue,
// ... assign many more values that I checked above
This seems like a lot of extra code to check every single value from the server. Is there a simpler solution?
Thank you for your time.
#Matt below. Here is the code in detail (took out some of the values in the userDictionary for brevity). I'm taking data from Facebook, adding additional info and saving it to Firebase:
//check all of the values
var birthdayValue:String? = "";
if let value:String? = data.objectForKey("birthday") as? String {
birthdayValue = value;
}
let userDictionary:NSMutableDictionary = [
"name": data.objectForKey("name") as! String,
"birthday": birthdayValue!,
"email": data.objectForKey("email") as! String,
"firstName": data.objectForKey("first_name") as! String,
"lastName": data.objectForKey("last_name") as! String,
"description": "",
"distance": 50,
"facebookID": data.objectForKey("id") as! String,
"location":[ 37.12314, -122.49182 ], //TODO: Get location
"points" : 0,
"rating" : 1,
"school" : "",
]
//we need to convert the FB profile pic to a base 64 string and add it here
let imagePath:String = "https://graph.facebook.com/\(data.objectForKey("id") as! String)/picture?width=375&height=667"
self.getDataFromUrl(NSURL(string: imagePath)!) { (imageData, response, error) -> Void in
//convert the data to base 64
let imgString:String = self.convertDataToBase64(imageData);
let images:Array<String> = [imgString];
userDictionary.setValue(images, forKey: "profilePics")
//save the user to Firebase
userRef.childByAppendingPath(data.objectForKey("id") as! String).setValue(userDictionary)
self.currentUserID = (data.objectForKey("id")) as! String
}
Swift actually support multiple types in a dictionary. The following is legal.
let arr: [NSObject: AnyObject] = [1: "hello", "a": 19, 2: "Swift"]
And you can store optional object in the dictionary:
let arr: [NSObject: AnyObject?] = [1: "hello", "a": 19, 2: nil]
And yes, you might need to check the existence of the value if you do care about it. Instead of if, I would use guard to make sure you can use the variable later.
guard let nameValue = data.objectForKey("name") as? String else {
return
}
// Now you can safely use nameValue.
In my app, I am receiving data from a server and mapping it to an NSMutableDictionary
There is no need for this at all. The data is coming to you as a dictionary (at least I presume it is; if it weren't, you could hardly be calling objectForKey, it seems to me). So now just proceed to use it. There is nothing to "map".
For example, suppose that data is the result of calling NSJSONSerialization's JSONObjectWithData:options: on an original NSData d. And suppose what you expect this to serialize to is a dictionary. So after you've said
var data = try! NSJSONSerialization.JSONObjectWithData(
d, options:[]) as! [NSObject:AnyObject]
...you are finished: that's a mutable dictionary and you are off to the races.
If your data is a mix of strings and numbers you could perhaps try to store it as AnyObject in the dictionary, e.g. [String: AnyObject]
Then you could just try to save your data like this:
var userDictionary: [String: AnyObject] = [:]
userDictionary["name"] = data.objectForKey("name")
And when you need to access it:
if let name = userDictionary["name"] as? String {
// Do something with name
}
If you don't want to save it as AnyObject I'd suggest you create a struct instead.
as! is a force downcast, which causes an exception if this downcast isn't possible (which obviously is the case if the value is nil). as?, on the other hand, only downcasts where possible, and results in nil otherwise (which sounds like what you want).
let userDictionary:NSMutableDictionary = [
"name": data.objectForKey("name") as? String,
... // many more values
]
This should work.
Edit: Never mind, this would only work if you'd use a native Swift dictionary. It's still useful to know about the difference between as? and as!, though.

Resources