SWIFT

Swift Lazy Var: Syntax, Behavior, and Practical Use Cases

A lazy variable in Swift is a stored property whose initialization is deferred until the first time it’s accessed. This can boost performance, reduce memory usage, and simplify the setup of properties that are expensive to create or depend on runtime conditions.

Lazy properties are especially useful for values that rely on external data, require access to self, or are only needed in specific scenarios. In larger applications, this feature can lead to more efficient and readable code.


What Is a Lazy Variable?

Lazy variables are declared using the lazy keyword before var. The value is computed and stored only once—the first time the property is accessed.

Here’s a basic example:

class DataManager {
    lazy var data: [String] = {
        print("Loading data...")
        return ["User1", "User2", "User3"]
    }()
}

The message is printed only when data is accessed for the first time. Future accesses return the same stored value without re-running the closure.


Syntax Overview

A typical declaration uses a closure to encapsulate initialization logic:

lazy var propertyName: Type = {
    // Initialization logic
    return someValue
}()

Example:

lazy var filePath: String = {
    let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    return documents.appendingPathComponent("notes.txt").path
}()

This ensures the path is calculated only when needed.


When to Use Lazy Properties

Lazy vars are best used when:

  • The property setup is time-consuming or resource-intensive (e.g. loading from disk or network)
  • You need access to self within the property’s initialization
  • The value isn’t always required during object setup
  • You want to reduce upfront memory usage

Using them thoughtfully helps keep your app responsive and resource-efficient.


Memory Behavior and Thread Safety

When declared, a lazy variable doesn’t occupy memory or run initialization code. Swift creates a placeholder. The actual value is computed and stored the first time the property is accessed.

As of Swift 4.0, this behavior is thread-safe—Swift ensures that the initialization closure runs only once, even if multiple threads try to access the property at the same time.


Lazy Var vs. Computed Property

It’s important to distinguish between these two:

  • A lazy variable is evaluated once and its result is stored.
  • A computed property recalculates its value every time it’s accessed and doesn’t store any data.

Example:

// Computed
var fullName: String {
    return "\(firstName) \(lastName)"
}

// Lazy
lazy var config: Config = {
    return Config.load()
}()

Use a lazy var when the setup is expensive and should happen only once.


Accessing self Inside Lazy Vars

One key benefit of lazy properties is that they allow access to self during initialization, which isn’t possible with regular stored properties.

class Profile {
    let name: String

    lazy var greeting: String = {
        return "Hello, \(self.name)"
    }()

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

This lets you create properties that depend on other parts of the object.


Restrictions in Structs

Lazy vars can only be used in classes. Attempting to use one in a struct will result in a compiler error:

struct Person {
    lazy var address = "Unknown" // ❌ Error
}

This is because structs are value types, and lazy initialization conflicts with how value types are copied.

If you need similar behavior in a struct, consider using a computed property instead.


Configuring Views and Objects

Lazy properties are often used for setting up UI components or other objects:

lazy var tableView: UITableView = {
    let table = UITableView()
    table.rowHeight = 44
    table.separatorStyle = .singleLine
    return table
}()

This pattern keeps configuration logic neatly encapsulated in one place.


Lazy Properties and Dependency Injection

They also work well with dependency injection, allowing resources to be initialized only when needed:

class AnalyticsService {
    func logEvent(_ event: String) { print("Logged \(event)") }
}

class AppController {
    lazy var analytics: AnalyticsService = {
        return AnalyticsService()
    }()
}

Here, the analytics instance is created only if it’s ever used.


Best Practices

Use lazy vars effectively by following these guidelines:

  • Use when needed, not just for convenience
  • Keep the initialization closure concise and readable
  • Favor them for costly setup or when access to self is necessary
  • Avoid lazy properties for values that will definitely be accessed right after initialization
  • Group related setup logic (e.g., for UI elements or service objects) into a lazy var to improve structure

Common Pitfalls

While powerful, lazy properties can cause issues if misused:

  • Declaring with let instead of var → compile-time error
  • Using them in structs → not allowed
  • Overcomplicating the closure → harder to debug or maintain
  • Using them in older Swift versions (< 4.0) → not thread-safe
  • Overusing them for simple values that don’t need lazy evaluation

Use laziness intentionally and only where it adds value.


Summary

Lazy variables let you defer property initialization until it’s actually needed. They improve performance, save memory, and make it easier to manage dependencies and configuration.

With thread-safe behavior, access to self, and support for closure-based setup, lazy vars are a powerful tool in Swift—especially for UI elements, injected services, or heavy resources. Just be mindful of when and where they’re appropriate, and you’ll write cleaner, more efficient code.

Learn to Code in Swift for Free
Start learning now
button icon
To advance beyond this tutorial and learn Swift by doing, try the interactive experience of Mimo. Whether you're starting from scratch or brushing up your coding skills, Mimo helps you take your coding journey above and beyond.

Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.

You can code, too.

© 2025 Mimo GmbH

Reach your coding goals faster