Use SwiftData @Model and ObservableObject/@Published in the same class? – Swift

by
Maya Patel
axios swift-concurrency swift-data swiftuinavigationlink

Quick Fix: In the last code snippet, change the score update score += 1 to practiceData.score += 1. Directly updating a local Int variable score will not trigger SwiftUI’s observation mechanism for @Model.

The Problem:

In Swift, while working with the SwiftData library, a user is attempting to utilize both @Model and ObservableObject/@Published within the same class. They are specifically trying to implement a property ‘latestResult’ that can be observed and updated in real time within a SwiftUI view. However, the user is encountering an error when annotating ‘latestResult’ with @Published. The user is also having difficulties updating the SwiftUI view with the new value of ‘latestResult’ after modifications are made.

The Solutions:

Solution 1: Use Model as your ObservableObject

SwiftData’s `@Model` meta-data declares that the annotated class is a model object.
This gives the class special abilities, including automatic persistence and MVVM support.
The MVVM support features in `SwiftData` include:

  • Conformity to the `ObservableObject` protocol.
  • The `@Published` property wrapper.
  • The `@ObservableObject` property wrapper can be used to declare a property on a `View`.
    When a published property inside the observed object changes, the SwiftUI view will automatically be invalidated and re-rendered.

    The `@Published` property wrapper can be used to mark a property as being observable.
    When the value of a published property changes, the SwiftUI view that uses that property will automatically be invalidated and re-rendered.

    To use `@Model` and `@Published` in the same class:

  • Declare your class with the `@Model` meta-data.
  • Use the `@Published` property wrapper on any properties that you want to be observable.
  • In your SwiftUI view, use the `@ObservedObject` property wrapper to observe the model object.
  • When the value of a published property in the model object changes, the SwiftUI view will automatically be invalidated and re-rendered.
  • // PracticeData.swift
    
    import SwiftData
    
    @Model
    final class PracticeData: ObservableObject, Codable {
        @Published var score: Int
        var results: [PracticeResult]
        @Published var latestResult: Bool?
        var trend: Float
    }
    
    // ContentView.swift
    
    import SwiftUI
    import SwiftData
    
    struct ContentView: View {
        @ObservedObject var practiceData = PracticeData()
    
        var body: some View {
            VStack {
                Text("Score: \(practiceData.score)")
    
                Button("Increase Score") {
                    practiceData.score += 1
                }
            }
        }
    }
    

    Q&A

    How do I use published and @Model at the same time?

    You don’t need to use them both, @Model also makes your class Observable.

    What is my mistake in this code snippet?

    You are updating a local variable score instead of the PracticeData.score

    How do I fix my mistake?

    Change score += 1 to practiceData.score += 1.

    Video Explanation:

    The following video, titled "Data Flow in a SwiftUI App - YouTube", provides additional insights and in-depth exploration related to the topics discussed in this post.

    Play video

    How to use @ObservableObject and @StateObject in SwiftUI | Bootcamp #50 ... Intro to SwiftData - Model, Container, Fetch, Create, Update & Delete.