Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.
According to wikipedia:
In computer programming, lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed.
That little quote pretty much sums up everything, however because we're working with the Swift programming language, we have a thing called optionals. If you don't know what are those, please read the linked articles first, and come back afterwards. 🤐
The ultimate guide of being lazy
When a property is only needed at some point in time, you can prefix it with the lazy keyword so it'll be "excluded" from the initialization process and it's default value will be assigned on-demand. This can be useful for types that are expensive to create, or needs more time to be created. Here is a quick tale of a lazy princess. 👸💤
The output of this code snippet is something like below, but as you can see the princess is sleeping for a very long time, she is also "blocking" the castle. 🏰
a new castle... zzz...sleeping... sleeping beauty is ready! castle is ready!
Now, we can speed things up by adding the lazy keword, so our hero will have time to slay the dragon and our princess can sleep in her bed until she's needed... 🐉 🗡 🤴
Much better! Now the castle is instantly ready for the battle, so the prince can wake up his loved one and... they lived happily ever after. End of story. 👸 ❤️ 🤴
a new castle... castle is ready! zzz...sleeping... sleeping beauty is ready!
I hope you enjoyed the fairy tale, but let's do some real coding! 🤓
Avoiding optionals with lazyness
As you've seen in the previous example lazy properties can be used to improve the performance of your Swift code. Also you can eliminate optionals in your objects. This can be useful if you're dealing with
UIView derived classes. For example if you need a
UILabel for your view hierarchy you usually have to declare that property as optional or as an implicitly unwrapped optional stored property. Let's remake this example by using lazy & eliminating the need of the evil optional requirement. 😈
It isn't so bad, however I still prefer to declare my views as implicitly unwrapped optionals. Maybe I'll change my mind later on, but old habits die hard... 💀
Using a lazy closure
You can use a lazy closure to wrap some of your code inside it. The main advantage of being lazy - over stored properties - is that your block will be executed ONLY if a read operation happens on that variable. You can also populate the value of a lazy property with a regular stored proeprty. Let's see this in practice.
This one is a nice practice if you'd like to declutter your init method. You can put all the object customization logic inside a closure. The closure executes itself on read (self-executing closure), so when you call
self.label your block will be executed and voilá: your view will be ready to use.
NOTE: that you can't use
self in stored properties, but you are allowed to do so with lazy blocks. Be careful: you should always use
[unowned self], if you don't want to create reference cycles and memory leaks. ♻️
Lazy initialization using factories
I already have a couple of articles about factories in Swift, so now i just want to show you how to use a factory method & a static factory combined with a lazy property.
If you don't like self-executing closures, you can move out your code into a factory method and use that one with your lazy variable. It's simple like this:
Now the factory method works like a private initializer for your lazy property. Let's bring this one step further, so we can improve reusability a little bit...
Outsourcing your lazy initializer code into a static factory can be a good practice if you'd like to reuse that code in multiple parts of your application. For example this is a good fit for initializing custom views. Also creating a custom view is not really a view controller task, so the responsibilities in this example are more separated.
As a gratis you can enjoy the advantages of static factory properties / methods, like caching or returning specific subtypes. Pretty neat! 👍
Lazy variables are a really convenient way to optimize your code, however they can only used on structs and classes. You can't use them as computed properties, this means they won't return the closure block every time you are trying to access them.
Another important thing is that lazy properties are not thread safe, so you have to be careful with them. Plus you don't always want to eliminate implicitly unwrapped optional values, sometimes it's just way better to simply crash! 🐛
Don't be lazy!
...but feel free to use lazy properties whenever you can! 😉