sesc360
sesc360

Reputation: 3255

Swift optionals for Dictionarys

MFSAPIManager().registerNewUser(userFirstName!, lastName: userLastName!, userTypeId: 1, email: userEmail!, password: userPassword!, completionHandler: { (response) in

        guard response.error == nil else {
            if let error = response.error {
                print(error)
            }
            dispatch_async(GlobalMainQueue) {
                self.displayAlertViewController(title: "Error", message: "Der Benutzer konnte nicht registriert werden")
            }
            return
        }

        if let serverResponse = response.value as? Dictionary<String,AnyObject> {
            let newUserReference = serverResponse["data"]!["userReference"]

            if (newUserReference != nil) {
                self.currentPLISTData["userReference"] = newUserReference
                dispatch_async(GlobalMainQueue) {
                    var myAlert = UIAlertController(title: "Alert", message: "Registrierung erfolgreich", preferredStyle: UIAlertControllerStyle.Alert)
                    let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: { (action) in
                        self.dismissViewControllerAnimated(true, completion: nil)
                    })
                    myAlert.addAction(okAction)
                    self.presentViewController(myAlert, animated: true, completion: nil)
                }
            }
        }


    })

I get an error message that I need to change:

let newUserReference = serverResponse["data"]["userReference"]

to

let newUserReference = serverResponse["data"]!["userReference"]

so unwrapping the ["data"] component. I am confused why this is the case?

Upvotes: 3

Views: 48

Answers (2)

user887210
user887210

Reputation:

Accessing a Dictionary<Key,Value> by subscript returns a Value?. This is because the key-value pair you are looking for might not be in the dictionary. Thus it returns an optional, set if it finds the key, unset if it doesn't.

I would handle it this way, using optional chaining:

if let serverResponse = response.value as? Dictionary<String,AnyObject>,
       newUserReference = serverResponse["data"]?["userReference"] {
  ...
}

By combining all the if..let statements into one you reduce the 'pyramid of doom'.

Eric D makes a good point that you should test the type of the first subscript, I would do it like this:

if let serverResponse = response.value as? Dictionary<String,AnyObject>,
       data = serverResponse["data"] as? [String:AnyObject],
       newUserReference = data["userReference"] {
  ...
}

Upvotes: 1

Eric Aya
Eric Aya

Reputation: 70098

A Swift dictionary always returns an Optional.

It means that to be able to subscript serverResponse["data"] with ["userReference"] you first have to unwrap serverResponse["data"].

It can be done unsafely, like in your example, or safely with something like if let:

if let data = serverResponse["data"] {
    let newUserReference = data["userReference"]
}

Also note that the compiler might not now the type of serverResponse["data"] so you may have to downcast it for being able to subscript to it, for example:

if let data = serverResponse["data"] as? [String:AnyObject] {
    let newUserReference = data["userReference"]
}

Upvotes: 4

Related Questions