chemistree apps

ios development

'If let' and thread safety

by Andy Riordan

I have a confession to make. The first time I saw if let syntax, I thought it was ugly, complex, and confusing. However, my opinion has changed, and I wanted to share some of my favorite things about it.

One major advantage to using if let is that it is naturally defensive against threading issues. When I starting using Swift, I saw a lot of third-party code online written like this, and was even tempted to adopt the style myself:

var postTitle: String?

func doSomethingWithAnOptionalValue() {
    if postTitle == nil {
        return
    }
    
    doStuffWith(postTitle!)
}

I can understand the appeal. While the indentation seems tame here, it can get pretty gnarly in more complex examples. The approach above avoids seemingly needless indentation, the creation of a local constant that you need to worry about, etc. Can you see the problem with that approach, however?

func doSomethingWithAnOptionalValue() {
    if postTitle == nil {
        return
    }
    // Let's say postTitle just got assigned nil on a different thread
    doStuffWith(postTitle!) // Crash
}

Yeah, that’s a problem. This brings up a different point - Apple made the ‘forced upwrapping’ operator ! for a reason. It should be something carefully considered, and avoided whenever possible.

In contrast, consider the if let approach:

func doSomethingWithAnOptionalValue() {
    if let unwrappedPostTitle = postTitle {
        doStuffWith(unwrappedPostTitle)
    }
}

If the variable got assigned nil or to another value while we’re inside the if statement, it doesn’t matter. We already have our constant reference pointing to the object the instance variable referred to when the if let was evaluated, and we already know it’s not nil.

I also got annoyed by the funky names I had to come up with for the unwrapped optional, as above. Luckily, Swift solves that for us with its (impressively flexible) scoping:

func doSomethingWithAnOptionalValue() {
    if let postTitle = postTitle {
        doStuffWith(postTitle)
    }
}

postTitle is now scoped to this if statement, so postTitle and self.postTitle are now two entirely different things.