Sunil Sharma Sunil Sharma - 4 months ago 16
Swift Question

How to parse unknown json data type in swift 2

While fetching data from api I can get response either array of products or dictionary with error for e.g.

If everything went right api sends array of products as:


[
"Product1":
{
name = "someting",
price = 100,
discount = 10%,
images = [image1,image2]
},
"Product2":
{
name = "someting",
price = 100,
discount = 10%,
images = [image1,image2]
}
]



But if some error occur it sends dictionary with error message and code as:


{
error_message = "message"
error_code = 202
}



I am using this code to convert JSON data to array:

do {
let jsonDict = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray{
//Some code....
} catch let error as NSError {
print("JSON Error: \(error.localizedDescription)")
}


but if I get error as dictionary it crash.

Problems:
1. How to know whether received data is an array or dictionary ?
2. Some time even key or value can be missing so checking for value it becomes very lengthy code like:

if let productsArray = jsonObject as? NSArray{
if let product1 = productsArray[0] as? NSDictionary{
if let imagesArray = product1["image"] as? NSArray{
if let imageUrl = imagesArray[0] as? String{
//Code ....
}
}
}
}


I read about guard keyword to reduce if condition but I don't have clear idea how to use here.

Answer

Problem 1: For try catch , add an if let for casting the object as NSDictionary or NSArray like :

 do {
    let jsonObject = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) 
    if let jsonDict = jsonObject as? NSDictionary {
       // Do smthg.
    } 
    if let jsonArray = jsonObject as? NSArray {
      // Do smthg. 
    }
 }catch {
 //...
 }

For Problem 2: I think guard won't help you . It needs smthg like return / break in its else statement. If you don't want to throw your methods if one of your values isn't available you have to use this lengthy if let code style. Maybe in your case best practice would be setting up a Data Model for Product with optional properties.

Class product {
 var name:String?
 var image:[NSData]? // maybe UIImage or smthg.
 var price:Int?
 var discount:Int?

  init(jsonDic:NSDictionary){
// if it's not there it would be nil
  self.name = jsonDic["name"] as? String 
  self.image = jsonDic["image"] as? NSArray
  self.discount = jsonDic["discount"] as? Int
  self.price = jsonDic["price"] as? Int
  }
}

Now you can load those models with your data without the if let etc.. But if you wanna read those values you have to use the if let for checkin if its not nil. For init in your case it should be something like this: Add this into the if let statement of the do catch block ( ... as? NSArray // DO smthg. )

for item in jsonArray {
  guard let jsonDic = item as? NSDictionary else { return }
 // if you dont know every key you can just iterate through this dictionary
    for (_,value) in jsonDic {
      guard let jsonDicValues = value as? NSDictionary else { return }
      productArray.append(Product(jsonDic: jsonDicValues) 
    }
}

As i said , know you got the whole if let stuff when reading from the model an not when writing ( reading the json )

Comments