When do you use optional chaining vs. if let or guard?

Short answer: I use optional chaining when I don’t really care if the operation fails; otherwise, I use if let or guard.

OK, so what does that look like in practice? Let’s look at an implementation of our favorite UITableViewDataSource method, tableView(_cellForRowAt:):

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
    
    cell.textLabel?.text = viewModel.title(forRowAt: indexPath)
    
    return cell
}

(This example comes straight from the Expanded Edition of Parsing JSON in Swift, so grab that if you want to see the full project.)

Here we’re using optional chaining on the cell’s textLabel to set the text if and only if the textLabel is non-nil. Why use optional chaining here? Because I don’t really care if the operation fails: I don’t care what happens if textLabel is nil because (a) I’ve never seen a nil textLabel on a UITableViewCell and (b) I’m not sure what I could do if I did see a nil textLabel. If this operation fails for some reason — and it’ll fail silently, since we’re using optional chaining — then we’ll see it when we run the app. We’ll have a cell (or a bunch of cells) without text on them, so we’ll know to debug this method to see what’s going on.

So what if we do care when a value is nil?

That’s usually a good time to use a guard statement that either prints the error or reports it even more loudly (by throwing an error, perhaps.)

When might we care about a value being nil? What about when we’re parsing JSON?

func repository(with dict: JSONDictionary) -> Repository? {
    guard let id = dict["id"] as? Int else {
        print("Error: Couldn't parse id property")
        return nil
    }
    
    guard let name = dict["name"] as? String else {
        print("Error: Couldn't parse name property for id: \(id)")
        return nil
    }
    
    return Repository(id: id, name: name)
}

It’s important for us to know if the “name” key is missing from our JSON — or if it’s the wrong type — so that we can figure out why and fix our JSON parser (or talk to the server team and ask them to fix the API).

Of course, you could turn these guards into if lets if that’s your preference, and the same principles apply: if you care about what’s nil, use if let or guard. Otherwise, optional chaining is easy and convenient.