Black Friday 2024 deal: 30% off our Swift and SwiftUI books! Learn more ...Black Friday 2024 deal:30% off our Swift and SwiftUI books >>

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 have older iOS apps and want to enhance them with modern SwiftUI features, check out my book Integrating SwiftUI into UIKit Apps. It provides detailed guidance on gradually adopting SwiftUI in your UIKit projects. Additionally, if you're eager to enhance your Swift programming skills, my latest book Swift Gems offers over a hundred advanced tips and techniques, including optimizing collections, handling strings, mastering asynchronous programming, and debugging, to take your Swift code to the next level.

I’m currently running a Black Friday 2024 promotion with 30% off my books, plus additional savings when purchased as a bundle. Visit the books page to learn more.

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

Black Friday 2024 offer: 30% off!$35$25

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
Black Friday 2024 offer: 30% off!

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$25