swiftswift2guard-statement

Guard when setting multiple class properties in Swift 2


It's trivial enough to do something like this:

class Collection {
    init(json: [String: AnyObject]){
        guard let id = json["id"] as? Int, name = json["name"] as? String else {
            print("Oh noes, bad JSON!")
            return
        }
    }
}

In that case we were using let to initialize local variables. However, modifying it to use class properties causes it to fail:

class Collection {

    let id: Int
    let name: String

    init(json: [String: AnyObject]){
        guard id = json["id"] as? Int, name = json["name"] as? String else {
            print("Oh noes, bad JSON!")
            return
        }
    }

}

It complains that let or var needs to be used but obviously that isn't the case. What's the proper way to do this in Swift 2?


Solution

  • In the if let, you are unwrapping values from the optional as new local variables. You can’t unwrap into existing variables. Instead, you have to unwrap, then assign i.e.

    class Collection {
    
        let id: Int
        let name: String
    
        init?(json: [String: AnyObject]){
            // alternate type pattern matching syntax you might like to try
            guard case let (id as Int, name as String) = (json["id"],json["name"]) 
            else {
                print("Oh noes, bad JSON!")
                self.id = 0     // must assign to all values
                self.name = ""  // before returning nil
                return nil
            }
            // now, assign those unwrapped values to self
            self.id = id
            self.name = name
        }
    
    }
    

    This is not specific to class properties - you can’t conditionally bind into any variable, for example this doesn’t work:

    var i = 0
    let s = "1"
    if i = Int(s) {  // nope
    
    }
    

    Instead you need to do:

    if let j = Int(s) {
      i = j
    }
    

    (though of course, in this case you’d be better with let i = Int(s) ?? 0)