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 or enum, making them static:

private enum 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 of a property 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 declared with 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
}()

What if my stack view depends on another property to configure itself? 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):

  • You do not need to write self when referencing other instance properties or methods within the closure.
  • The closure is not escaping so you do not need to do the weak self dance to avoid creating a retain cycle.
  • Be careful if your property can be accessed by multiple threads before it is initialized. There is no guarantee that it will be initialized only once if several threads access the property at the same time before the initial value is set.