AzamSharp

Lazy Properties in Swift

Swift language allows you to create several different types of properties, including computed, property observers and even lazy properties. In this article, we will learn how lazy properties can provide performance benefits for time consuming calculations.

Implementation

Let’s consider a scenario, where we are responsible for creating questions for an exam. The Exam struct is implemented below:

enum Level {
    case easy
    case medium
    case hard
}

struct Exam {
    let level: Level
    
    var questions: [String] {
        
        sleep(5) // simulating an API call :) 
        
        switch level {
            case .easy:
                return ["1+1", "1+2", "2+3"]
            case .medium:
                return ["12+31", "41+52", "32+13"]
            case .hard:
                return ["122+311", "411+522", "332+143"]
        }
    }
}

let exam = Exam(level: .easy)
print(exam.questions) // this will take 5 seconds to load questions
print(exam.questions) // this will also take 5 seconds

The most important thing to note is the questions property. There is a sleep(5) function, which is used to simulate an API call. Based on the difficulty level, we return appropriate questions to the user.

After creating an Exam instance, if we try to access questions then it will take 5 seconds. *This is not an issue, but when we try to access the questions again, it will again take 5 seconds. The actual questions have not changed but anytime we access the value, it always take 5 seconds.

This is a perfect scenario to use lazy properties. Lazy properties are initialized only when they are accessed and the result is stored and returned instantly for future access.

Keep in mind that lazy properties are not initialized when an instance is created.

Implementing Using Lazy Properties

Converting our computed properties to lazy properties is pretty straight forward. This is shown in the implementation below:

struct Exam {
    var level: Level
    
    lazy var questions: [String] = {
        
        sleep(5) // simulating an API call :)
        
        switch level {
            case .easy:
                return ["1+1", "1+2", "2+3"]
            case .medium:
                return ["12+31", "41+52", "32+13"]
            case .hard:
                return ["122+311", "411+522", "332+143"]
        }
    }()
}

var exam = Exam(level: .easy)
print(exam.questions) // this will take 5 seconds to load questions
print(exam.questions) // this will be returned instantly

Lazy properties are decorated with the lazy keyword. The = indicates that it is a closure and the closure is called at the end of the definition. If you execute the above code, you will notice that the first call to questions takes 5 seconds but all future calls are returned instantly. This is because once the lazy property is initialized, it returns the results instantly without executing the body of the property.

You can also make sure that lazy properties cannot be set from outside. This is accomplished by using the private(set) identifier as shown below:

lazy private(set) var questions: [String] = {

The modifier indicates that questions can only be set from inside the Exam struct and if you try to set it from outside, you will receive an error.

exam.questions = ["What is the name of our planet?"]
// Cannot assign to property: 'questions' setter is inaccessible

Quiz

Now, that you have learned about lazy properties. Take a look at the code below and see if you can answer the question.

var exam = Exam(level: .easy)
print(exam.questions)
var hardExam = exam
hardExam.level = .hard

print(hardExam.questions) // What questions will be display? easy or hard and why?

Conclusion:

Lazy properties allows you to store the result of time consuming operations and then later access it without paying the performance penalty. Go ahead and take a look at your existing project and see if it can benefit from using lazy properties.

*This is not an issue: If your API is taking 5 seconds to return then it is recommended that you fine tune your API call. For the sake of this article, we are not considering 5 second an issue.