Required vs Optional vs Implicitly unwrapped optional in Swift

Swift iOS Variables Feb 7, 2021 · 5 min read

I know there are quite a lot of articles about this topic on the Internet. Also, you can read about it on Swift.org. So, you may ask "Why I ever need another article on this topic?" Well, I try to approach this topic differently based on my real experience writing and reviewing others' code and try to show nuances that you may not find in other places. I don't want to re-state what it is already written and don't want to pollute the Internet.

In Swift, variables can be required, optional, or implicitly unwrapped optional. You either use required or optional variables based on your app's domain logic. As for implicitly unwrapped optional, the short answer is "Never use it". Please, don't. But, there are only 2 cases I know where it can be used. I only use one of them just for convenience and it is outside the main codebase where it doesn't crash my apps on my customers' phones.

When you define a required variable, you either set its type explicitly, assign another required variable, or assign a value from which its type can be inferred. The compiler ensures that a required variable has a value when you access it and raises an error at compile-time when you try to assign nil or another optional variable.

var required: Int = 1 // The same as `var required = 1`
required += 2 // 3
required = nil // Compile-time error: 'nil' cannot be assigned to type 'Int'

When you define an optional variable, you either set its type explicitly with a ? suffix or assign another optional or implicitly unwrapped optional variable to it. It means it either holds a value or nil. To check this, you must unwrap it with either if let or if var or even with a ?? nil-coalescing operator. The compiler raises an error at compile-time when you try to do any operation other than printing it with the print function or assigning it to another optional or implicitly unwrapped optional variable.

var optional: Int? = 1

if var optional = optional {
    optional += 2 // 3
}

optional = nil

if var optional = optional {
    optional += 3 // Not executed because it is assigned to `nil`
}

optional += 3 // Compile-time error: Value of optional type 'Int?' must be unwrapped to a value of type 'Int'

An implicitly unwrapped optional variable is the same as an optional one but raises an error at run-time if it holds nil when it is accessed. It means "I, as a human being, ensure that it won't be nil and crash your app when you use it". Isn't it scary? I am just using an example with value types. It gets even scarier with reference types whose values can be mutated globally inside an app where it is used. Always keep in mind that you are trading safety, the cornerstone of Swift language, for convenience when you use implicitly unwrapped optionals instead of optionals that is against the philosophy of Swift language. I recommend you reject pull requests with the usage of implicitly unwrapped optionals immediately. You may even configure tools like SwiftLint in a way so that it raises a compile-time error for implicitly unwrapped optionals instead of showing a warning which is by default.

var implicitlyUnwrappedOptional: Int! = 1

if var implicitlyUnwrappedOptional = implicitlyUnwrappedOptional {
    implicitlyUnwrappedOptional += 2 // 3
}

implicitlyUnwrappedOptional = nil

if var implicitlyUnwrappedOptional = implicitlyUnwrappedOptional {
    implicitlyUnwrappedOptional += 3 // Not executed because it is assigned to `nil`
}

implicitlyUnwrappedOptional += 3 // Run-time error: Unexpectedly found nil while implicitly unwrapping an Optional value

So, when should you use implicitly unwrapped optionals? As I mentioned before, there are 2 cases when you can or maybe should use them. But, keep in mind that you can always replace them with optionals.

The first one is when the system itself automatically manages the lifecycle of a variable from its initialization to deinitialization such as Interface Builder (xib, Storyboard, etc). It is just for convenience. You can make it optional and unwrap it or use Optional Chaining when you want to access it and its properties. But, are you still using the Interface Builder in 2021 when you can code yourself with AutoLayout or use SwiftUI? I ditched it after I used it for the first time a long time ago.

import UIKit

class CustomViewController: UIViewController {
    @IBOutlet var titleLabel: UILabel!
}

The other one is when the system provides a template and dictates how to initialize and deinitialize variables such as XCTest.

import XCTest

final class DatabaseConnectionTests: XCTestCase {
    var connection: DatabaseConnection!

    override class func setUp() {
        super.setUp()

        // Initialize variables
        connection = DatabaseConnection(host: "127.0.0.1", port: 5432).connect()
    }

    override class func tearDown() {
        // Deinitialize variables
        connection.disconnect()
        connection = nil

        super.tearDown()
    }
}

But, what enrages me, even more, is when I see developers trying to avoid optional variables when they make sense, especially with String types, but make them required instead in order not to mess with if let or if var. Please, don't. Required and optional variables are something your app's business logic dictates, not you. Here is an example of User entity class. The email property is not required when users register so it is likely to be nil when you retrieve a user's data from your database.

// Some developers do this
class User {
    var firstName: String
    var lastName: String
    var email: String

    init(firstName: String, lastName: String, email: String = "") {
        self.firstName = firstName
        self.lastName = lastName
        self.email = email
    }
}

// instead of this
class User {
    var firstName: String
    var lastName: String
    var email: String?

    init(firstName: String, lastName: String, email: String? = nil) {
        self.firstName = firstName
        self.lastName = lastName
        self.email = email
    }
}

This misleads other developers to think that the email is a required property according to business requirements.

I hope you only use required or optional variables based on your app's logic instead of for convenience. Additionally, don't use implicitly unwrapped optionals at all other than the 2 cases I mentioned. I also hope Apple will ditch implicitly unwrapped optionals once for all in the future release of Swift language so that developers can avoid them.