Alamofire in Swift: converting response data in usable JSON dictionary - json

I would like to handle a JSON string that is returned as data from a HTTP call made using Alamofire.
This question uses SwiftyJSON.
However I wanted to go a little bit "lower level" and understand how to convert the response object into a dictionary.
Reasoning is that it feels that a dictionary may be a simple / easy way to access to the JSON values in the response (rather than having to go through the process of converting the response to a JSON object).
This is under the assumption that JSON objects and dictionaries are the same thing (are they?).
Here is the sample function that I wrote:
func question() -> Void{
let response : DataRequest = Alamofire.request("http://aapiurl", parameters: nil)
// Working with JSON Apple developer guide:
// https://developer.apple.com/swift/blog/?id=37
response.responseJSON { response in
if let JSON = response.result.value
{
print("JSON: \(JSON)") // Works!
let data = JSON as! NSMutableDictionary
// Casting fails
// Could not cast value of type '__NSCFArray' (0x19f952150) to 'NSMutableDictionary' (0x19f9523f8).
print("Data: \(data)")
}
}
}
EDIT:
The JSON object seems to be of type Any and does not have any of the methods that are suggested in the answers below.
I have tried to convert it to a Dictionary and got the error below:

A JSON object IS a Dictionary (or possibly an Array at top level).
Note that you should not be using NSMutableDictionary or NSDictionary (or NSArray or NSMutableArray) in Swift.
Also, JSON objects are not working objects. JSON is a way to move data around. It is not to be used as a data source.
If you want to edit the information you get from JSON then you should construct proper data objects from that JSON and work with them.
If you then need to send JSON from this new data then you take your data objects and convert them back to dictionaries and arrays (i.e. JSON objects) and send that data.

Alamofire has the result value of type Any because it usually would be an array or a dictionary. In Swift you normally shouldn't use NS* classes, but rather native swift types such as Array and Dictionary.
You can (should) use optionals to look into the returned JSON object:
if let array = response.result.value as? [Any] {
print("Got an array with \(array.count) objects")
}
else if let dictionary = response.result.value as? [AnyHashable: Any] {
print("Got a dictionary: \(dictionary)")
}
...
Depending on what you expect from your backend, you can treat each of the cases as a success or a failure.

Alamofire.request(myUrl)
.responseJSON {
response in
if let dict = response.result.value as? [String : Any] {
debugPrint(dict)
wishLists.removeAll() //[[String:Any]]
let lists = dict["wishlists"] as! [String: Any]
debugPrint(lists)
for (key, value) in lists {
var list = value as! [String: Any]
wishLists.append(list)
}
debugPrint(wishLists)
self.tableView.reloadData()
}
}

Related

SWIFT: Iterate through a JSON object that's convert to a dictionary

NOTE: Somewhat similar questions have been already asked. And yet none of them provides how to solve this seemingly simple task. So I hope it gets resolved here once and for all.
MY PROBLEM:
I am receiving this nested JSON object:
print("type(of: JSON) \( type(of: JSON))") //__NSDictionaryI
completion(true, nil, JSON as? [String: Any], nil)
Alamofire module converts it, as you see, to a Dictionary.
I have been trying for hours on end to access the nested values with different methods (Something, I thought that should be straightforward compared to JavaScript), inside this Dictionary but I couldn't find a single way that works.
So except for the high-level values, I couldn't access anything else:
for (key,movieData) in moviesData! { // moviesData is the JSON dictionary object
// Do some logic
}
So is there anyway to easily manipulate/access the received JSON data?
This is a far from being an ideal solution but it works. So here it is:
for (key,movieData) in moviesData! {
if let nestedDictionary = moviesData![key] as? [String: Any] {
print("nestedDictionary: ",nestedDictionary) // logs the result part of the JSON object
// Trying to access nested values
for (key,value) in nestedDictionary {
print("nestedDictionary.key: ",key)
print("nestedDictionary.type(of:value): ",type(of:value) )
print("nestedDictionary.value: ",value)
// Testing if sections is an array of dictionaries
if let arrayOfDictionaries = nestedDictionary[key] as? [[String: Any]] {
print("nestedDictionary: ",nestedDictionary) // logs the result part of the JSON object
// Trying to access array items which are sections
for item in arrayOfDictionaries {
print("arrayOfDictionaries.item: ",item)
}
}
}
}
}
It is based on the tip mentioned here:
if let dictionary = jsonWithObjectRoot as? [String: Any] {
if let number = dictionary["someKey"] as? Double {
// access individual value in dictionary
}
for (key, value) in dictionary {
// access all key / value pairs in dictionary
}
if let nestedDictionary = dictionary["anotherKey"] as? [String: Any] {
// access nested dictionary values by key
}
}
The key idea is to check if there's a nested dictionary or a nested array inside the json and then iterate accordingly.

Difficulty Parsing JSON With Alamofire

I have this snippet of code:
Alamofire.request("https://api.waqi.info/feed/geo:10.3;20.7/?token=demo").responseJSON { response in
print(response.result) // result of response serialization
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
guard let JSON = response.result.value as? [String:Any],
let data = JSON["data"] as? [[String:Any]] else {
print("Could not parse weather values")
return
}
It seemed to be working a few days ago but now when I run the application it will print the Could not parse weather values indicating that it is not parsing the JSON data correctly. I've gone back and changed it to what it used to be but it seems to be broken still.
I'm hoping someone here will be able to help me out with this as it's a crucial component of my first project that will be published to the App Store.
EDIT: Just to add, it successfully prints the JSON data at the print("JSON: \(JSON)") line
Need I post my response word for word from the last time you posted this question?
You can parse your result following the printout of your JSON
response:
guard let JSON = response.result.value as? [String:Any],
let weather = JSON["weather"] as? [[String:Any]] else {
print("Could not parse weather values")
return
}
for element in weather {
if let description = element["description"] as? String {
print(description)
}
}
If you wanted to save the result, or do something based on a certain
result as you described, you could insert something else instead of
just print(description) like:
if description == "sunny" {
//do something
}
Let me know if this makes sense. Keep in mind that ( and ) in the
Xcode console means "Array".
I really ought to be flagging this as a duplicate of Save Alamofire Result as Variable?
Edit: Based on this JSON snippet - http://pastebin.com/XiGhNA26 - it should be easy to parse out the desired information with:
guard let JSON = response.result.value as? [String:Any],
let data = JSON["data"] as? [String:Any] else
{
print("Could not parse weather values")
return
}
The difference is that in this JSON response, the "data" parameter is not an array of dictionaries, it is just a dictionary itself.
In you guard statement you are doing the same thing twice. That's not necessary. Take a look at this code and test it out. I'm not able to test your code right now, but I believe the error is around the area of where the downcasting happens. I believe improving that will help you.
Alamofire.request("https://api.waqi.info/feed/geo:10.3;20.7/?token=demo").responseJSON { response in
print(response.result) // result of response serialization
if let JSON = response.result.value {
print("JSON: \(JSON)")
let parsed = JSON as? NSDictionary //Here is your son data ready to be used
//We can access the data content
let data = parsed["data"] //The data is here and can be access using keys
print("Data: \(parsed["data"])")
}else{
print("Values don't exists")
return
}
}

Iterate over AnyObject. Error: Type 'AnyObject' does not conform to protocol 'SequenceType'

I'm using Alamofire to get a data from a JSON file. Example of the output:
[{"image_name":"vacation"},{"image_name":"graduation"}]
I have a problem when I try to access the information from the JSON output.
Alamofire.request(.GET, url).responseJSON { (response) -> Void in
if let JSON = response.result.value {
for json in JSON{
print(json)
}
}
The problem I have is that my JSON output is an AnyObject and I cannt iterate over an AnyObject. If I do the following:
print(JSON[0]["image_name"])
Then I can see the output correctly. How can I iterate over an AnyObject?
You may need to explicitly state the type of JSON as an array of dictionaries:
if let JSON = response.result.value as [[String : AnyObject]] {
// ...
}

Problems parsing json String to objects in Swift

I googled a lot, pretty much copied code i found online from tutorials to simply parse a json String in Swift to useable objects.
Code:
func parseJson(json: String) -> [AnyObject] {
let data = json.dataUsingEncoding(NSUTF8StringEncoding)
do {
if let array = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? [AnyObject] {
return array
}
}
catch {
// Error hanndling here
}
return [AnyObject]()
}
Json String im trying to parse:
"response":{"loggedIn":false,"message":"Some errormessage here"}}
What happens:
The program won't jump into the if let array = ... It stops there since it can't parse the string to json (or AnyObject) and will simply go to return AnyObject.
Why does this happen and how do i fix it?
Adjust your code a little to allow for better debugging:
func parseJson(json: String) -> [AnyObject] {
let data = json.dataUsingEncoding(NSUTF8StringEncoding)
do {
let parsed = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
if let array = parsed as? [AnyObject] {
return array
}
}
catch {
print(error)
}
return [AnyObject]()
}
Two changes there:
Printing any error caught.
Doing JSONObjectWithData and the as? conversion in two separate steps.
Pasting this in a playground quickly reveals an error being caught: "JSON text did not start with array or object and option to allow fragments not set." Your JSON fragment is missing the opening {.
Once that problem is fixed, you’ll see that parsed gets set, but the subsequent if let array = parsed as? [AnyObject] falls through. That’s because your top-level element is a dictionary, not an array, so casting to [AnyObject] fails.

Swift - Reading JSON File

I'm new to Swift - trying to read a JSON file from a URL. My attempt below.
The JSON looks valid - I tested it with JSONLint but it keeps crashing.
Thoughts?
func getRemoteJsonFile() -> NSDictionary {
//Create a new url
let remoteUrl:NSURL? = NSURL(string: "http://nfl-api.azurewebsites.net/myplayers.json")
//check if its nil
if let actualRemoteUrl = remoteUrl {
//try to get the data
let filedata:NSData? = NSData(contentsOfURL: actualRemoteUrl)
//check if its nil
if let actualFileData = filedata {
//parse out the dictionaries
let jsonDict = NSJSONSerialization.JSONObjectWithData(actualFileData, options: NSJSONReadingOptions.AllowFragments, error: nil) as NSDictionary
return jsonDict
}
}
return NSDictionary()
}
This took me a second to figure out, so I don't blame you for missing it.
The JSON you linked to is minified, so it's difficult to see the structure. Let's take a look at (a fragment of) it after piping it through a prettifier:
[
{
"PlayerId":2501863,
"PlayerName":"Peyton Manning",
"PlayerTeam":"DEN",
"PlayerPosition":"QB",
"PlayerPassingYards":4727,
"PlayerPassingTDs":39,
"PlayerInterceptions":15,
"PlayerRushingYards":-24,
"PlayerRushingTDs":0,
"PlayerReceivingYards":0,
"PlayerReceivingTDs":0,
"PlayerReturnYards":0,
"PlayerReturnTDs":0,
"PlayerFumbleTDs":0,
"PlayerTwoPointConversions":2,
"PlayerFumblesLost":2,
"PlayerTeamLogo":"http://i.nflcdn.com/static/site/7.0/img/logos/teams-gloss-81x54/den.png"
}
]
Huh. It's encased in brackets, which means that it's an array.
It's an array, so you can't cast it as an NSDictionary. Instead, you could cast it as an NSArray, but why not use native Swift types?
Well, if you don't like types, you're about to find out, but I still think that this is a better way, because it forces you to think about the data you're parsing.
So we have the first part of our type definition for this function; it's an array ([]). What components is our array made up of? We could go with a simple NSDictionary, but we're doing full native types here, so let's use a native Swift dictionary.
To do that, we have to know the types of the dictionary (the syntax for a native dictionary type is [KeyType: ValueType]). Examining the JSON shows that all of the keys are Strings, but the values are of varying types, so we can use AnyObject.
That gives us a dictionary type of [String: AnyObject], and our entire JSON is an array of that, so the final type is [[String: AnyObject]] (wow).
Now that we have the proper type, we can modify the function you're using to parse the JSON a bit.
First of all, let's use our new type for the return and cast values. Then, let's make the return type optional in case something goes wrong and add an error variable to document that.
A cleaned up function would look something like this:
func getData() -> [[String: AnyObject]]? {
let data: NSData? = NSData(contentsOfURL: NSURL(string: "http://nfl-api.azurewebsites.net/myplayers.json")!)
if let req: NSData = data {
var error: NSError?
if let JSON: [[String: AnyObject]] = NSJSONSerialization.JSONObjectWithData(req, options: NSJSONReadingOptions.AllowFragments, error: &error) as? [[String: AnyObject]] {
return JSON
}
}
return nil
}
That's it!
We can now call the function and extract values from our [[String: AnyObject]] (again, wow) like this:
if let data: [[String: AnyObject]] = getData() {
println(data[0]["PlayerName"]!) // Peyton Manning
}
Update your code with this:
func getRemoteJsonFile() -> [NSDictionary] {
// Create a new URL
let remoteUrl:NSURL? = NSURL(string: "http://nfl-api.azurewebsites.net/myplayers.json")
let urlString:String = "\(remoteUrl)"
// Check if it's nil
if let actualRemoteUrl = remoteUrl {
// Try to get the data
let fileData:NSData? = NSData(contentsOfURL: actualRemoteUrl)
// Check if it's nil
if let actualFileData = fileData {
// Parse out the dictionaries
let arrayOfDictionaries:[NSDictionary]? = NSJSONSerialization.JSONObjectWithData(actualFileData, options: NSJSONReadingOptions.MutableContainers, error: nil) as [NSDictionary]?
if let actualArrayOfDictionaries = arrayOfDictionaries {
// Successfully parsed out array of dictionaries
return actualArrayOfDictionaries
}
}
}
return [NSDictionary]()
}
This is working fine for me.