Extracting values from anyobject dictionary into array - ios

My other question was marked as being identical to another but it didn't answer the problem I was having.
Here is the code:
if status == "OK" {
self.selectedRoute = (dictionary["routes"] as! Array<Dictionary<NSObject, AnyObject>>)[0]
self.overviewPolyline = self.selectedRoute["overview_polyline"] as! Dictionary<NSObject, AnyObject>
let legs = self.selectedRoute["legs"] as! Array<Dictionary<NSObject, AnyObject>>
let steps = legs[0]["steps"]!
for i in 0...steps.count - 1 {
let step_coordinate = steps[i]["start_location"]!
print(step_coordinate!["lat"]!!)
}
}
The print statement gives this result
40.7609205
40.7640121
40.7595325
40.7501637
40.7481923
40.7393448
40.7252038
40.7225337
40.718295
but if I swap the print statement out to grab the values and put them in an array I get the fatal error: found nil.
Can someone explain why this is the case? How can I grab these values if not through a for loop?

Here's a cleaner version of your code.
Rules:
Don't force unwrap anything!
Optionally cast values coming from [NSObject: AnyObject] lookups.
Use guard statements and if let to safely unwrap optionals.
Don't index arrays that might be empty without checking first.
if status == "OK" {
guard let routes = dictionary["routes"] as? [[NSObject: AnyObject]] else { return }
self.selectedRoute = routes.first ?? [:]
self.overviewPolyline = self.selectedRoute["overview_polyline"] as? [NSObject: AnyObject] ?? [:]
guard let legs = self.selectedRoute["legs"] as? [[NSObject: AnyObject]] else { return }
let firstleg = legs.first ?? [:]
guard let steps = firstleg["steps"] as? [[NSObject: AnyObject]] else { return }
for step in steps {
if let step_coordinate = step["start_location"] as? [NSObject: AnyObject] {
if let lat = step_coordinate["lat"] as? Double {
print(lat)
// append lat to array of lats
lats.append(lat)
}
}
}
}

Related

Loop through an optional object in Swift 4

I have this code
let dic = snapshot.value as! [String:[String:Any]]
Array(dic.values).forEach {
print($0["fcmToken"])
}
It produce these 3
Optional(c1DdtdDF1Rs:APA91bGJBUD65nidQiFDO90AVNgq0wiMjUaZmZXVJ8c_tYmFe5dkmgweOdO10jzPRlMVZF_qNyWMMsu7EhA5IMVo3jLWvBThDteR7WWUPqau-ZFAHKQPHgI5Vb48vA-_4nwkZCKrOVoT)
Optional(c1DdtdDF1Rs:APA91bGJBUD65nidQiFDO90AVNgq0wiMjUaZmZXVJ8c_tYmFe5dkmgweOdO10jzPRlMVZF_qNyWMMsu7EhA5IMVo3jLWvBThDteR7WWUPqau-ZFAHKQPHgI5Vb48vA-_4nwkZCKrOVoT)
Optional(c1DdtdDF1Rs:APA91bGJBUD65nidQiFDO90AVNgq0wiMjUaZmZXVJ8c_tYmFe5dkmgweOdO10jzPRlMVZF_qNyWMMsu7EhA5IMVo3jLWvBThDteR7WWUPqau-ZFAHKQPHgI5Vb48vA-_4nwkZCKrOVoT)
I want to loop through them and add a simple if-check.
I tried
let dic = snapshot.value as! [String:[String:Any]]
Array(dic.values).forEach {
print($0["fcmToken"])
if($0["fcmToken"] != fcmToken) {
print("token is not match detected")
}
}
I kept getting
Binary operator '!=' cannot be applied to operands of type 'Any?' and 'String?'
How would one go about debugging this further?
You have:
let dic = snapshot.value as! [String:[String:Any]]
Array(dic.values).forEach {
print($0["fcmToken"])
if($0["fcmToken"] != fcmToken) {
print("token is not match detected")
}
}
But it’s giving you a compile error.
Assuming that this line is working:
let dic = snapshot.value as! [String:[String:Any]]
I'd write the rest like this:
for v in dic.values {
if let token = v["fcmtoken"] as? String, token != fcmtoken {
print("token \(token) is not match detected")
}
}
The effect is the same and there’s no error.
You can try
let dic = snapshot.value as! [String:[String:Any]]
let tokens = Array(dic.values).map { $0["fcmToken"] as! String }
let exists = tokens.contains(fcmToken)
Your problem as $0["fcmToken"] is of type Any? that can't compared with type String? ( fcmToken )
let dic = snapshot.value as! [String:[String:Any]]
Array(dic.values).forEach {
print($0["fcmToken"])
if let token = $0["fcmToken"] as? String , token != fcmToken {
print("token is not match detected")
}
// or
if ($0["fcmToken"] as? String) != fcmToken {
print("token is not match detected")
}
}
You can savely use
guard let dic = snapshot.value as? [String: [String:Any]] else {
return
}
but it won't function when the value is nil

Swift executing firebase calls out of order

My code makes calls to my firebase database, but the order in which it receives the data is incorrect in terms of the function call. It calls the data from ref3 then ref2 then ref4 and I would like for it to retrieve the data in order of ref2, ref3, ref4 of course. No matter what it will always do it in this order.
var ref2: DatabaseReference?
var ref3: DatabaseReference?
var ref4: DatabaseReference?
ref2 = Database.database().reference().child("User data").
ref3 = Database.database().reference().child("User Info").child("Name")
ref4 = Database.database().reference().child("User Info").child("Address")
ref2?.observe(DataEventType.value, with:{(DataSnapshot) in
if DataSnapshot.childrenCount > 0{
for data in DataSnapshot.children.allObjects as![DataSnapshot]{
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["Username"] as! String
let n: String = proObj?["User login"] as! String
}
}
})
ref3?.observe(DataEventType.value, with:{(DataSnapshot) in
if DataSnapshot.childrenCount > 0{
for data in DataSnapshot.children.allObjects as![DataSnapshot]{
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["User first name"] as! String
let n: String = proObj?["User last name"] as! String
}
}
})
ref4?.observe(DataEventType.value, with:{(DataSnapshot) in
if DataSnapshot.childrenCount > 0{
for data in DataSnapshot.children.allObjects as![DataSnapshot]{
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["User email"] as! String
}
}
})
When querying data from your firebase database, you are performing an asynchronous call. To put things in simple terms, your code is executed on a different thread and, subsequently, performs parallel operations. This is exactly what is happening in your case.
You are observing data from three different references, and even though you have defined their sequence programmatically, nothing guarantees that the code within the completion handler blocks of your observers will run in that exact same sequence.
If you want to run them sequentially, then you have to nest your observers so that the next database query is executed only after the previous one has finished.
The below should hypothetically work
ref2?.observe(DataEventType.value, with: { (DataSnapshot) in
if DataSnapshot.childrenCount > 0 {
for data in DataSnapshot.children.allObjects as! [DataSnapshot] {
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["Username"] as! String
let n: String = proObj?["User login"] as! String
}
}
ref3?.observe(DataEventType.value, with: { (DataSnapshot) in
if DataSnapshot.childrenCount > 0 {
for data in DataSnapshot.children.allObjects as! [DataSnapshot] {
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["User first name"] as! String
let n: String = proObj?["User last name"] as! String
}
}
ref4?.observe(DataEventType.value, with: { (DataSnapshot) in
if DataSnapshot.childrenCount > 0 {
for data in DataSnapshot.children.allObjects as![DataSnapshot] {
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["User email"] as! String
}
}
}) // ref4 observer
}) // ref3 observer
}) // ref2 observer

How to iterate array of dictionaries having nested dictionary?

I have JSON
http://maps.googleapis.com/maps/api/directions/json?&origin=28.594517,77.049112&destination=28.654109,77.34395&travelMode=DRIVING&provideRouteAlternatives=false&sensor=false
I want to access all the values in distance which is in steps.
Here is my code
class Router {
var getValue = Double()
func downloadData(complete: downloadComplete) {
let currentURL = URL(string:getURL)!
Alamofire.request(currentURL).responseJSON { (responseJSON) in
let result = responseJSON
// print(result)
if let dict = result.value as? Dictionary<String,AnyObject>{
if let routes = dict["routes"] as? [Dictionary<String, AnyObject>]{
if let legs = routes[0]["legs"] as? [Dictionary<String, AnyObject>]{
if let steps = legs[0]["steps"] as? [Dictionary<String, AnyObject>]{
for index in steps{
if let distance = steps["distance"] as? Dictionary<String,AnyObject>{
if let value = distance["value"] as? Double{
self.getValue = self.getValue + value
print(self.getValue)
}
}
}
}
}
}
}
}
complete()
}
I am getting error in:
if let distance = steps["distance"] as? Dictionary<String,AnyObject>{
which says:
Cannot subscript a value of type '[Dictionary]' with an index of type string
How can I access this?
You are trying to access the value of an array of dictionaries by key. You should change steps["distance"] to index["distance"].
if let distance = index["distance"] as? Dictionary<String,AnyObject>{
if let value = distance["value"] as? Double{
self.getValue = self.getValue + value
print(self.getValue)
}
}
The error is clear.
steps["distance"] is an array of dictionaries, which can be accessed like steps[0],steps[1].
You need to change following line
if let distance = steps["distance"] as? Dictionary<String,AnyObject>
to
if let distance = index["distance"] as? Dictionary<String,AnyObject>

How to properly set up a For- In Loop within a Guard statement?

I'm trying to set up a loop to retrieve info from inside a json dictionary but the dictionary is in a guard statement:
guard let resultsDictionary = jsonDictionary["result"] as? [[String : Any]]?,
let costDictionary = resultsDictionary?[0],
let cost = costDictionary["cost"] as? [String: Any],
let airbnb = cost["airbnb_median"] as? [String: Any]{
for air in airbnb {
let airbnbUS = air["USD"] as Int
let airbnbLocal = air["CHF"] as Int
}
else {
print("Error: Could not retrieve dictionary")
return;
}
When I do this I get multiple errors:
Expected 'else' after 'guard' condition,
Variable declared in 'guard' condition is not usable in its body,
Braced block of statements is an unused closure
I'm not sure why it doesnt work
The syntax for guard is:
guard [expression] else {
[code-block]
}
You want to use if instead:
if let resultsDictionary = jsonDictionary["result"] as? [[String : Any]]?,
let costDictionary = resultsDictionary?[0],
let cost = costDictionary["cost"] as? [String: Any],
let airbnb = cost["airbnb_median"] as? [String: Any]{
...for loop here...
} else {
...error code here...
}
Or you can say:
guard let resultsDictionary = jsonDictionary["result"] as? [[String : Any]]?,
let costDictionary = resultsDictionary?[0],
let cost = costDictionary["cost"] as? [String: Any],
let airbnb = cost["airbnb_median"] as? [String: Any] else {
...error code here...
return // <-- must return here
}
...for loop here, which will only run if guard passes...
Here you should use if let like:
if let resultsDictionary = jsonDictionary["result"] as? [[String : Any]]?,
let costDictionary = resultsDictionary?.first,
let cost = costDictionary["cost"] as? [String: Any],
let airbnb = cost["airbnb_median"] as? [String: Any] {
for air in airbnb {
let airbnbUS = air["USD"] as Int
let airbnbLocal = air["CHF"] as Int
...any other statements...
}
} else {
print("Error: Could not retrieve dictionary")
return
}
This can you help to decide when to use guard

Ambiguous use of subscript. Array Swift IOS

I try to compile on device but i get this error. Any help?. In the simulator works perfectly.
I get an ambiguous use of subscript error in the following code and was hoping somebody else has encountered this and know the fix.
case .Success:
if response.response?.statusCode == 200 {
print ("Respuesta 200")
if let value = response.result.value {
let respuestaJSON = JSON(value)
let objsonUSUARIOS = respuestaJSON["d"].object
let arrayUsuarios = objsonUSUARIOS["results"]!
//print ("Usuarios: ",String(arrayUsuarios))
for i in 0 ..< arrayUsuarios!.count{
let boletines = boletinJSON()
if let item = arrayUsuarios![i] as? [String: AnyObject]{
)
if let person = item["Title"] as? String
{
boletines.name = person
}
if let person = item["Portada"] as? String
{
boletines.imagen = person
}
if let person = item["Created"] as? String
{
boletines.fecha = person
}
if let person = item["AttachmentFiles"] as? [String: AnyObject] {
if let itemAttach = person["__deferred"] as? [String: AnyObject]{
if let itemdeferred = itemAttach["uri"] as? String {
boletines.urldescarga = itemdeferred
}
}
}
self.boletin.append(boletines)
self.view.hideToastActivity()
}
}
}
self.tableView.reloadData()
// self.view.hideToastActivity()
}
Inform the compiler what the intermediary object objsonUSUARIOS is of type
let objsonUSUARIOS = respuestaJSON["d"].object
After the above statement, the compiler does not know what kind of object he is dealing with. So make sure that you can actually do all the casting as below
let objsonUSUARIOS = respuestaJSON["d"].object as! Dictionary
let arrayUsuarios = objsonUSUARIOS["results"]! as! Array
The problem is that you have not specified the type of object arrayUsuarios is Array, so try to explicit type cast the arrayUsuarios Array
let arrayUsuarios = objsonUSUARIOS["results"] as! [[String: AnyObject]]

Resources