2

I have an application that is using the Foursquare API to download JSON data. I am using NSURLSession and the dataTaskWithRequest with completion block method to fetch the data. I am getting the data fine but sometimes a nested array named groups can be empty. And when I am parsing through the JSON like below, for some reason my conditional statement is not handling the empty array like I am expecting it should. Instead of evaluating the array as empty and proceeding to the "else" portion of the if let...else statement if instead throughs a runtime error stating: index 0 beyond bounds of empty array

if let response: NSDictionary = data["response"] as? [String: AnyObject],
            groups: NSArray = response["groups"] as? NSArray,
                        // Error here \|/ (sometimes groups array is empty)
            dic: NSDictionary = groups[0] as? NSDictionary,
            items: NSArray = dic["items"] as! NSArray {

}

else {

    // Never gets here. Why?
    // IF the groups array is empty, then the if let should return 
    // false and drop down to the else block, right?
}

I am relatively new to Swift, can someone tell me why this is happening and what I can do to fix this? Thanks

Brandon A
  • 8,153
  • 3
  • 42
  • 77
  • have you tried nsnull checking the array response object values? – Larry Pickles Aug 29 '15 at 08:13
  • meaning actually testing for the class type NSNull? – Larry Pickles Aug 29 '15 at 08:13
  • I know I can do something like that, I'm just trying to understand Swift here. It just seems to me if it is empty (meaning false) that it would evaluate as such. And when if statements usually do that they then drop to the else block. I guess I'm a little confused here with Swift's if let. – Brandon A Aug 29 '15 at 08:16
  • well, you can try this out, this is from Matt (top poster here on IOS tag): or card:AnyObject in arr { switch card { // how to test for different possible types case let card as NSNull: // do one thing case let card as Card: // do a different thing default: fatalError("unexpected object in card array") // should never happen! } } http://stackoverflow.com/questions/24026609/detect-a-null-value-in-nsdictionary – Larry Pickles Aug 29 '15 at 08:21
  • I always NSNull check, but vadian, down below in his answer may have the answer for you as well, it's something related to that value being null or what vadian has posted, it happns in Obj C as well, not just swift – Larry Pickles Aug 29 '15 at 08:22

3 Answers3

4

You have to check explicitly outside an if letstatement if the array is empty, because

An empty array is never an optional

if let response = data["response"] as? [String: AnyObject], groups = response["groups"] as? NSArray {
  if !groups.isEmpty {
    if let dic = groups[0] as? NSDictionary {
       items = dic["items"] as! NSArray
       // do something with items
       println(items)
    }
  }
} else ...

You can omit all type annotations while downcasting a type

However, you can perform the check with a where clause, this works in Swift 1.2 and 2

if let response = data["response"] as? [String: AnyObject],
                  groups = response["groups"] as? [AnyObject] where !groups.isEmpty,
                  let dic = groups[0] as? NSDictionary,
                  items = dic["items"] as? NSArray {
   // do something with items
   println(items)
} else {...
vadian
  • 274,689
  • 30
  • 353
  • 361
  • I figured this is what I was going to have to do and already did this. I just thought with the power of Swift that the intended purpose for the if let would in fact deal with my condition. Thanks for your answer. – Brandon A Aug 29 '15 at 09:50
0

Use && operator between statments.

For example this won't crash:

var a = 4;
var c = 5;
var array = Array<Int>();

if  a > 2 &&  c > 10 && array[0] != 0 {

}
nsinvocation
  • 7,559
  • 3
  • 41
  • 46
0

Make sure the array is not empty before trying to access it:

if let response: NSDictionary = data["response"] as? [String: AnyObject],
    let groups: NSArray = response["groups"] as? NSArray where groups.count > 0 {
        if let dic: NSDictionary = groups[0] as? NSDictionary,
            let items: NSArray = dic["items"] as? NSArray {
             // Do something..
             return // When the 2nd if fails, we never hit the else clause
        }
}

// Do logic for else here
...
Yariv Nissim
  • 13,273
  • 1
  • 38
  • 44