Swift Lazy Property Initialization

What is a lazy stored property in Swift? How and when should you use it? A quick guide to get you started and some key points to remember.

First a recap of the most common ways to declare and initialize stored properties in Swift:

Direct Assignment

The simplest form of initializer for constant and variable stored properties by direct assignment of an intial value:

// constant
let fontSize: CGFloat = 24.0

// variable
var spacing: CGFloat = 16.0

// Optional
var title: String?

Note that optionals are initialized to nil by default. I like to collect constant magic numbers like fontSize into a struct, making them static:

private struct ViewMetrics {
  static let fontSize: CGFloat = 24.0
  ...
 }

 titleLabel.font = ViewMetrics.fontSize

Using An Initializer

if you do not assign an initial value to a stored property as part of its definition you must set it within an initializer. To quote from The Swift Programming Language guide:

Classes and structures must set all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created. Stored properties cannot be left in an indeterminate state.

class Book {
  let title: String

  init(title: String) {
    self.title = title
  }
}

When a property always takes the same initial value prefer to set it when you declare the property rather than in an initializer.

Using A Closure

When the initial value needs more setup a closure can be a good choice. I find the closure style to be preferable to lots of initializer code as it keeps the setup close to the property declaration.

let button: UIButton = {
  let button = UIButton(type: .system)
  button.titleLabel?.font = UIFont.systemFont(ofSize: ViewMetrics.fontSize)
  ...
  return button
}()

The closure is called once when the type containing the property is initialized. The return value of the closure is assigned to the property. Do not forget the trailing () to execute the closure.

The Lazy Way

Lazy initialization is often used when the initial value is relatively expensive to create. You only create the value when you are sure you need it. If you are coming to Swift from Objective-C you may be familiar with the technique of using a getter to lazily initialize a property only when it is first used:

- (NSNumberFormatter *)decimalFormatter
{
  if (_decimalFormatter == nil)
  {
    _decimalFormatter = [[NSNumberFormatter alloc] init];
    [_decimalFormatter setNumberStyle:NSNumberFormatterDecimalStyle];        
  }
  return _decimalFormatter;
}

So what about lazy properties in Swift? Swift properties do not have a backing instance variable (like _decimalFormatter). To lazily initialize a property in Swift you add the lazy keyword:

lazy var decimalFormatter: NumberFormatter = {
  let formatter = NumberFormatter()
  formatter.numberStyle = .decimal
  return formatter
}()

Note that lazy properties are always var. The closure is called when the property is accessed which can be after initialization completes.

Accessing Self

Another common use for lazy property initialization is when the initial value depends on a property or method of the initialized instance. For example, this stack view is fine as a let constant property using a closure to setup the object:

let stackView: UIStackView = {
  let stackView = UIStackView()
  stackView.spacing = ViewMetrics.spacing
  return stackView
}()

Remember that the closure is called during initialization so you cannot use self to access any properties or methods of the instance yet. If you need to access self you must replace the let with lazy var:

var spacing: CGFloat = 16.0  {
    didSet {
        stackView.spacing = spacing
    }
}

lazy var stackView: UIStackView = {
  let stackView = UIStackView()
  stackView.spacing = spacing
  ...
  return stackView
}()

The closure is now executed after the object is initialized so you have full access to instance properties and methods via self. This is a convenient way to declare things like user interface components. Move any common configuration to a separate method or class extension:

var buttonFontSize: CGFloat = 18.0 { ... }

private lazy var redButton: UIButton = {
  return UILabel.colorButton(title: "Red", color: .red, fontSize: buttonFontSize)
}()

private lazy var blueButton: UIButton = {
  return UILabel.colorButton(title: "Blue", color: .blue, fontSize: buttonFontSize)
}()

Points To Remember

A few key points to remember that are not obvious and often cause confusion (to me at least):

Never miss a post

iOS Size Classes Cheat Sheet

Subscribe and also get my free iOS Size Classes Cheat Sheet

Success! Now check your email to confirm your subscription and download your free guide to iOS Size Classes.

There was an error submitting your subscription. Please try again.

Unsubscribe at any time. See Privacy Policy
No time to watch WWDC videos?

Sign up to get my iOS posts and news direct to your inbox and also get my free iOS Size Classes Cheat Sheet

OK! Check your inbox (or spam folder) for an email to confirm your details and download your free guide to iOS Size Classes.

There was an error submitting your subscription. Please try again.

Unsubscribe at any time. See Privacy Policy
Archives Categories