NEW BOOK! SwiftUI Fundamentals: The essential guide to SwiftUI core concepts and APIs. Learn more ...NEW BOOK! SwiftUI Fundamentals:Master SwiftUI core concepts and APIs. Learn more...

Lazy vars in @Observable classes in Swift

When migrating from the ObservableObject protocol to the @Observable macro, I encountered an issue with using a lazy variable in my observable class. In this post, I’ll explain the problem and show how I resolved it.

Here’s a simplified version of my class using ObservableObject:

class WalksViewModel: ObservableObject {
    let walks = [
        Walk(title: "Central Park Loop", difficulty: .easy),
        Walk(title: "Mountain Ridge Trail", difficulty: .hard),
        Walk(title: "Riverbank Stroll", difficulty: .medium)
    ]
    
    @Published var sortingIsOn = false
    
    lazy var sortedWalks: [Walk] = {
        walks.sorted(
            using: KeyPathComparator(\Walk.difficulty)
        )
    }()
}

This works as expected. However, after updating the class to use the @Observable macro, the following error appeared: 'lazy' cannot be used on a computed property.

@Observable
class WalksViewModel {
    let walks = [
        Walk(title: "Central Park Loop", difficulty: .easy),
        Walk(title: "Mountain Ridge Trail", difficulty: .hard),
        Walk(title: "Riverbank Stroll", difficulty: .medium)
    ]
    
    var sortingIsOn = false
    
    // Error: 'lazy' cannot be used on a computed property
    lazy var sortedWalks: [Walk] = {
        walks.sorted(
            using: KeyPathComparator(\Walk.difficulty)
        )
    }()
}

This happens because the @Observable macro automatically generates observation logic for all variables in the class, but it cannot synthesize the necessary observation code for a lazy property.

While searching for a solution, I found this discussion on Swift Forums. I learned that to use lazy variables in observable classes, we can annotate them with @ObservationIgnored. This tells the observation system to skip synthesizing observation logic for the property.

Here’s how my final solution looks:

@Observable
class WalksViewModel {
    let walks = [
        Walk(title: "Central Park Loop", difficulty: .easy),
        Walk(title: "Mountain Ridge Trail", difficulty: .hard),
        Walk(title: "Riverbank Stroll", difficulty: .medium)
    ]
    
    var sortingIsOn = false
    
    @ObservationIgnored
    lazy var sortedWalks: [Walk] = {
        walks.sorted(
            using: KeyPathComparator(\Walk.difficulty)
        )
    }()
}


If you're an experienced Swift developer looking to level up your skills, take a look at my book Swift Gems. It offers 100+ advanced Swift tips, from optimizing collections and leveraging generics, to async patterns, powerful debugging techniques, and more - each designed to make your code clearer, faster, and easier to maintain.

Swift Gems by Natalia Panferova book coverSwift Gems by Natalia Panferova book cover

Level up your Swift skills!$35

100+ tips to take your Swift code to the next level

Swift Gemsby Natalia Panferova

  • Advanced Swift techniques for experienced developers bypassing basic tutorials
  • Curated, actionable tips ready for immediate integration into any Swift project
  • Strategies to improve code quality, structure, and performance across all platforms

Level up your Swift skills!

100+ tips to take your Swift code to the next level

Swift Gems by Natalia Panferova book coverSwift Gems by Natalia Panferova book cover

Swift Gems

by Natalia Panferova

$35