Swift 3 JSON NSFastEnumerationIterator has no subscript members - ios

Im using Swift 3 and Alamofire 4.0. I am able to print out the entire response but I am having trouble looping through and printing out each value. I am getting a " Type 'NSFastEnumerationIterator.Element' (aka 'Any') has no subscript members when I try to print out 'title' below. Any help is greatly appreciated.
Alamofire.request(url).responseJSON { response in
if let dict = response.result.value as? Dictionary<String, AnyObject> {
if let datas = dict["data"] as? NSArray{
for data in datas{
print("DEVELOPER: \(data)")
if let title = data["myTitle"] as? String{
print(title)
}
}
}
}
}

Just use native Swift Array. Use always Swift native types unless you have absolutely no choice. NSArray lacks type information so the compiler cannot infer that the array contains dictionaries.
if let datas = dict["data"] as? [[String:Any]] {

Sometimes you wanna keep your data as structured, all you have to do is check for the dictionary itself while in loop like the following:
for apple in apples {
if let _ = apple as? [String:AnyObject] {
// do whatever you like here
}
}

Related

Parse JSONArray contains JSONObjects Swift

hello i'm new to swift and I have responseJson from alamofire consist of jsonArray contain jsonObjects like this
[{"id":"1","name":"person1"},{"id":"2","name":"person2"}]
how i can parse it into array of this custom model
class Person {
var name : String
var id : String
}
i've done a much searching but can't find case identical to mine and i can't use Codable because i'm using xcode 8 and not able to upgrade my xcode version to 9 now
I'm getting the response like this
Alamofire.request(url).responseJSON{ response in
if(response.result.isSuccess)
{
if let jsonarray = response.result.value as? [[String: Any]]
{
//what to do here ?
}
}
}
if let jsonarray = response.result.value as? [[String: Any]]{
//what to do here ?
var persons:[Person] = []
for userDictionary in jsonarray{
guard let id = userDictionary["id"] as? String, let name = userDictionary["name"] as? String else { continue }
persons.append(Person(id, name))
}
//Persons complete.
}
Use guard else for the required variables.
If there are additional variables that could be optional, like var age:Int? in Person, you could do like this:
for userDictionary in jsonarray{
guard let id = userDictionary["id"] as? String, let name = userDictionary["name"] as? String else { continue }
let age = userDictionary["age"] as? Int
persons.append(Person(id, name, age))
}
#Tony,
In swift4 you can codable protocol to parsing the JSON that helps in writing generic code. Suppose if in future requirement came to add dob than its very simple.
And in swift3 you can use object mapper class for same.
If you need more help than please let me know.

Parsing array of json object in ios swift 3.0 [duplicate]

This question already has answers here:
Swift 3 JSON NSFastEnumerationIterator has no subscript members
(2 answers)
Closed 6 years ago.
Trying to parse a JSON array in iOS swift 3.0. But in XCode I always get to see this error:
Type 'NSFastEnumerationIterator.Element' (aka 'Any') has no subscript members
The array I am trying to parse looks something like this:
[{"area_code":1,"area_name":"value"},{"area_code":2,"area_name":"value"},{"area_code":3,"area_name":"value"},{"area_code":4,"area_name":"value"}]
The code snippet of iOS looks something like this.
let json = try!JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as? NSArray
// The JSONObjectWithData constructor didn't return an error. But, we should still
// check and make sure that json has a value using optional binding.
if let parseJSON = json {
for dayData in parseJSON{
let areaObj = AreaCode()
if let areaCode = dayData["area_code"] as? Int{
areaObj.areaCode = areaCode
}
if let areaName = dayData["area_name"] as? String{
areaObj.areaName = areaName
}
areaCodeArray.append(areaObj)
}
DispatchQueue.main.async(execute: {
// perform on main
self.onGetAreaList("Success");
});
}
Try this way using [[String:Any]] instead NSArray
let json = try! JSONSerialization.jsonObject(with: data!, options: []) as? [[String:Any]]
for dayData in json{
let areaObj = AreaCode()
if let areaCode = dayData["area_code"] as? Int{
areaObj.areaCode = areaCode
}
if let areaName = dayData["area_name"] as? String{
areaObj.areaName = areaName
}
areaCodeArray.append(areaObj)

IOS swift how can i check for empty array while getting Json data

I am very new to swift and in my code I get JSON from a certain email, the JSON data is first put into an Array in swift, however if the array is null or there is no Json Data I get this error
Could not cast value of type 'NSNull' (0x1067a5748) to 'NSArray' (0x1067a4c58).
I been trying to fix this by reading other posts such as Check if optional array is empty but it is not working. The issue comes with this code below
session.dataTask(with:request, completionHandler: {(data, response, error) in
if error != nil {
print(error)
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any]
if let musicJob = parsedData["music"] as! [AnyObject]? {
for S in musicjob {
if let genre = S["genre"] as? String {
self.genres.append(genre)
}
}
}
If the Json is null I get the error on this line right here
if let musicJob = parsedData["music"] as! [AnyObject]?
The musicJob counts the number of objects such as "30 values or 44 values " etc . If there is no Json then it returns nil and it crashes . Is there anyway that I can catch the value of nil or an empty array so that the app does not crash ? Again everything works as long as the musicJob array is not empty .
Try this:
if let musicJob = parsedData["music"] as? [AnyObject]
You can check both nil and empty for Array like this.
if let musicJob = parsedData["music"] as? [Any], !musicJob.isEmpty {
print(musicJob)
}
Yes. You can always use conditional unwrapping. Force unwrapping is usually dangerous (crash) and should be used with great care.
First of all, using guard to make sure preconditions are met before doing anything is a good practice.
if guard let responseData = data else {
assertionFailure("response data is nil") // no need for assertionFailure is data is expected to be nil sometimes
return
}
And something similar with the line that crashes:
if let musicJob = parsedData["music"] as? [AnyObject] {
....
}

Accessing Nested NSDictionary values in Swift 3.0

I have the following data I received from Firebase. I have made my snapshotValue a NSDictionary.
self.ref.child("users").child(facebookID_Firebase as! String).observeSingleEvent(of: .value, with: { (snapshot) in
let snapshotValue = snapshot.value as? NSDictionary
print(snapshotValue, "snapshotValue")
//this line of code doesn't work
//self.pictureURL = snapshot["picture"]["data"]["url"]
}) { (error) in
print(error.localizedDescription)
}
I tried How do I manipulate nested dictionaries in Swift, e.g. JSON data? , How to access deeply nested dictionaries in Swift , and other solutions yet no luck.
How do I access the url value inside the data key AND the picture key?
I can make another reference in Firebase and get the value, but I'm trying to save another request. 😓
When you refrence to a key of a Dictionary in swift you get out an unwrapped value. This means it can be nil. You can force unwrap the value or you can use pretty if let =
this should probably work.
if let pictureUrl = snapshot["picture"]["data"]["url"] {
self.pictureURL = pictureUrl
}
Try using :-
if let pictureDict = snapshot.value["picture"] as? [String:AnyObject]{
if let dataDict = pictureDict.value["data"] as? [String:AnyObject]{
self.pictureURL = dataDict.value["url"] as! String
}
}
Inline dictionary unwrapping:
let url = ((snapshot.value as? NSDictionary)?["picture"] as? NSDictionary)?["url"] as? String
You can use the following syntax, that is prettier:
if let pictureDict = snapshot.value["picture"] as? [String:AnyObject],
let dataDict = pictureDict.value["data"] as? [String:AnyObject] {
self.pictureURL = dataDict.value["url"] as! String
}
}

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.
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 )
You have a few things going on here, one, I would analyze your server's http response status code and only attempt to process data if you received a status code indicating you will have good data
// In practical scenarios, this may be a range
if statusCode != 200 {
// Handle a scenario where you don't have good data.
return
}
Secondly, I'd guard against the response, it looks like you have named it "data" like so:
guard let receivedData = data else {
return
}
From this point on, you can use the receivedData constant.
Here'd I'd attempt to use NSJSONSeralization, like you do, but by casting it into a Swift dictionary, like so:
if let responseDictionary = try? NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? [String:AnyObject] {
// Here you can try to access keys on the response
// You can try things like
let products = responseDictionary?["products"] as? [[String:AnyObject]]
for product in products {
let productName = product["name"] as? String
if productName == nil {
continue
}
let newProduct = Product(name: productName)
// Do something with newly processed data
}
}
I tried to be general and also show you a guard example.
first of all I recommend using SwiftyJSON pod or the class straight into your Xcode, it works like a charm and you won't need to cast things down to figure whether you have a string or a dictionary or whatever. It is gold.
Once you've got your JSON, you can use this recursive function I created that does exactly what you need. It turns any Json into a dictionary. I mostly use it to save data into Firebase, without having to parse everything.
After you have imported SwiftyJSON into your project and added import SwiftyJSON to your Swift file you can:
//JSON is created using the awesome SwiftyJSON pod
func json2dic(_ j: JSON) -> [String:AnyObject] {
var post = [String:AnyObject]()
for (key, object) in j {
post[key] = object.stringValue as AnyObject
if object.stringValue == "" {
post[key] = json2dic(object) as AnyObject
}
}
return post
}
let json = JSON(value) // Value is the json structure you received from API.
var myDictionary = [String:AnyObject]()
myDictionary = json2dic(json)
You can catch the class of your response. If your response is kind of class dictionary, assign it as dictionary else if your response is kind of class array, assign it to array. Good luck.

Resources