Leah Sapan
Leah Sapan

Reputation: 3791

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?

Upvotes: 7

Views: 2816

Answers (1)

Airspeed Velocity
Airspeed Velocity

Reputation: 40965

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)

Upvotes: 14

Related Questions