How To: Lazy Computed Properties In Swift

Written by: Reinder de Vries, September 6 2017, in App Development

Lazy Computed Properties In Swift

Lazy computed properties make your Swift development more efficient and easier to read. It’s a bit of syntactic sugar around the lazy keyword and computed properties, as you’ll find out in this article.

Here’s what you’ll learn:

  • What computed properties are, and when you should use them
  • What lazy initialization is good (or bad) for…
  • How and why to use lazy computed properties

We all know lazy programmers are the best programmers, so you’ll want to pay close attention in this blog post. What’s that Bill Gates quote again?

I choose a lazy person to do a hard job. Because a lazy person will find an easy way to do it.

Ain’t that the truth! Let’s get to it…

  1. What Are Computed Properties?
  2. How To Use Lazy Initialization
  3. Lazy Computed Properties
  4. Further Reading

What Are Computed Properties?

Y’all know what a property is, right? It’s a variable that’s part of a class object. You use it to store stuff. You also use properties to structure your code better.

Here’s a class that defines 3 properties:

class Person {
    var legs = 2
    var eyes = 2
    var arms = 2
}

Here’s how you use it:

var person = Person()
person.legs = 3

See how I changed the property legs to 3?

OK, now let’s get into computed properties. Just like functions, computed properties can calculate a value and then return it.

Like this:

class Circle {
    var radius:Double = 0

    var circumference:Double {
        return Double.pi * 2 * radius
    }
}

That’s a class called Circle with two properties. The first one radius, with type Double, is pretty ordinary. The second property, circumference of type Double, is a computed property.

Instead of storing a value it returns a value by executing a bit of code. In the example above, the circle circumference is calculated as C = 2 π r.

You can now use the above Circle class like this:

var cookie = Circle()
cookie.radius = 42.0
print(cookie.circumference)

See how you can just type cookie.circumference and get the calculated result of the property?

Computed properties have one downside: every time you access them, they’re re-calculated.

This can be a disadvantage if the value of the property doesn’t change and its calculation is intensive. You’d recalculate the value when you don’t have to! That’s a waste of computer resources.

Is there a way to solve that?

Get 5 of my best practices

Get 5 of my best practices

Learn how to build better iOS apps

I’ll show you exactly how I built a dozen professional iOS apps,
write extensible Swift code, and turn coffee into code.
Wait, what? Yup – into Swift code.

How To Use Lazy Initialization

OK, let’s talk about initialization for a bit. Every time you create an object in Swift, it’s initialized.

You can describe “initialization” as filling a variable with an initial value. You know an object can have properties, so when you initialize an object, its properties are initialized too.

Let’s say I have a car with an advanced GPS navigation system. When I’m taking the car some place I know well, I don’t need navigation – I can just drive there. However, if I’d just initialize the car, the navigation system would be initialized as well – even if I don’t use it. That’s a waste of resources!

class Car {
    var navigation = GPS()
}

var car = Car()
// car.navigation is now initialized too :-(

How can I use my car’s resources more sensibly? With lazy initialization!

With lazy initialization, also called lazy loading, the initial value of a property is only calculated when it’s first used. If it’s not used, it’s not calculated.

Lazy initialization is helpful in two scenarios:

  • When the value of the property is dependent on factors unknown at initialization of the object
  • When the initial value of the property requires complex computations or is intensive to calculate

In both scenarios it doesn’t make sense to initialize the property when the object is initialized, so that’s when you use lazy initialization.

Using lazy initialization is easy: simply prepend the property with the lazy keyword. You can only use lazy as a variable, so with var and not with let.

Like this:

class Car {
    lazy var navigation = GPS()
}

Why don’t you try it out for yourself? Execute the code below in Xcode, Playgrounds or a Swift Sandbox. First, run the code with lazy. Then, remove the lazy and see the difference in output.

class GPS {
    init() {
        print("Initializing GPS...")
    }
}

class Car {
    lazy var navigation = GPS()
}

var car = Car()

When you run the above code with the lazy property navigation, that Initializing GPS... isn’t printed when car is initialized on the last line. When you remove lazy, the property navigation is initialized when car is initialized, and therefore it prints Initializing GPS....

Awesome!

So how does this connect back to computed properties? Well… you can’t lazy load computed properties. This, for instance, doesn’t work:

class Circle {
    var radius:Double = 0

    lazy var circumference:Double {
        return Double.pi * 2 * radius
    }
}

// Output: 'lazy' may not be used on a computed property

See how I made circumference lazy? It doesn’t work…

Build better iOS apps by mastering best practices and app architecture » Find out how

Lazy Computed Properties

There’s a trick, however, to make lazy computed properties. Here, check this out:

class Circle {
    var radius:Double = 0

    lazy var circumference:Double = { [unowned self] in
        return Double.pi * 2 * self.radius
    }()
}

A few things have changed:

  • The property circumference uses lazy initialization with the lazy keyword
  • The circumference property now uses a closure to calculate the circumference of the circle
  • The closure uses a capture list to access the property radius on self

The closure itself is invoked when the circumference property is accessed, because of that last (). Because it’s lazy, the closure is only executed once when the property is accessed for the first time. That’s a lazy computed property!

The trick is combining a closure and lazily invoking it. When you access the circumference property you implicitly call the closure, and get its result.

Quick Tip: Lazy computed properties are only calculated once: when they’re first accessed. Depending on your code, and code you work with, this is a Good Thing or a hidden cause of weird bugs!

Lazy computed properties come in handy in a few scenarios:

  • When it doesn’t make sense to set an initial value when initializing the object, for instance because it depends on unknown outside factors.
  • When a computed property is computationally intensive, for instance when it calculates something complex

Lazy computed properties also have another added benefit: syntactic sugar.

Sometimes it’s just nicer and more elegant to define a property as a lazy computed property, instead of writing it as a function. After all, you can code a function in such a way that it’ll only recalculate a returned value if it hadn’t done so before – right?

Keywords like lazy, and closures, can add to the readability and maintainability of your code. With lazy computed properties you can keep your code closer together, adding a property’s logic near the property declaration itself. Neat!

It’s good to be lazy in Swift, like this: https://learnappmaking.com/lazy-computed-properties-swiftClick To Tweet

Further Reading

Lazy computed properties – so now you know!

Lazy initialization postpones initializing a property until it’s first accessed. Using the lazy keyword isn’t allowed for computed properties, so you use an implicitly invoked closure to get the same effect.

Want to learn more? Check out these resources:

Enjoyed this article? Please share it!

How To: Lazy Computed Properties In SwiftClick To Tweet

Reinder de Vries

Reinder de Vries is an indie app developer who teaches aspiring app developers and marketers how to build their own apps at LearnAppMaking.com. Since 2009 he has developed over 50 apps for iOS, Android and the web, and his code is used by millions of users all over the globe. When Reinder isn't building apps, he enjoys strong espresso and traveling.

Comments & Questions

Got a comment or question? Let me know! I read and respond to every one. Thanks!