Hongxu Jin
Hongxu Jin

Reputation: 807

Confused about optional value in swift

I'm new to swift and I don't really understand how to use optional value correctly.

The situation is: Firstly, I have a model class to store some values of its properties and send the request to server to get values. code:

import Foundation

class User: NSObject {

    var name: String?

    func getInfo(updateUI: () -> ()) {
        let manager = AFHTTPSessionManager()
        manager.POST(URLString, parameters: nil, success: { (task: NSURLSessionDataTask, responseObject: AnyObject?) in

            let dic = responseObject as? NSDictionary
            self.name = dic?.objectForKey("name")
            updateUI()

        }, failure: { (task: NSURLSessionDataTask?, error: NSError) in
            log.obj(error)
    })

}

Secondly, I want to use this model in a ViewController to get values from server and update UI. code:

class UserViewController: UIViewController {

    @IBOutlet var nameLabel: UILabel!
    var user = User()

    override func viewDidLoad() {
        super.viewDidLoad()

        user.getInfo({
            nameLabel.text = user.name!
        })
    }

}

I think it's a dangerous way to handle this work because there is nothing can ensure that I have a certain name when I want to put it on the label. It works pretty well in most cases but it can crash when fail to get a right value from server(for example, there is no value for key "name").

I have two ideas about questions above:

  1. Give a default value for the properties like: var name = ""
  2. Test the value before unwarp it like:

    if let foo = user.name {
        nameLabel.text = foo
    }
    

I want to choose the first way because:

  1. I think showing a default string on the label is much more acceptable than crash because fail to unwarp a optional value.
  2. When I have a lot more values to use, the code will be very long and diffcult to read.

Did I choose a right one? Or both of them didn't understand the usage of optional value and there is a better way? Someone can help me?

Upvotes: 2

Views: 183

Answers (2)

Matt K
Matt K

Reputation: 51

Your instincts are correct that force unwrapping optional is ill-advised. You are also correct that providing a non-nil default value defeats the purpose of an optional.

if let foo = user.name binding is a sound way of doing this, but if all you're doing is assigning a value, you can spare yourself the curly braces by using the nil coalescing operator, as pbush25 states:

nameLabel.text = user.name ?? "Default Label Text"

This means "unwrap user.name and if its value is not nil, use it. Otherwise, use the following value:" You can even chain them together if, for example, you wanted to use user's ID as the fallback label text, but the ID might also not exist:

nameLabel.text = user.name ?? user.id ?? "No name or ID".

Even if you want to crash in the case that the optional value is nil, it would be better to assert or use a preconditionFailure() than to simply force unwrap the optional so that you can at least provide an error message.

Upvotes: 3

pbush25
pbush25

Reputation: 5258

You can safely unwrap an optional or give it a default value without declaring a new variable like so:

nameLabel.text = user.name ?? ""

This is called the nil coalescing operator and will try to unwrap an optional, and if the unwrap is unsuccessful will give it the default value that you have placed on the rhs of the operator.

Upvotes: 3

Related Questions