SwiftedMind
SwiftedMind

Reputation: 4287

SwiftData - How to observe changes to the database outside of SwiftUI?

I have a simple example. I fetch all Trip objects from my database:

import SwiftData

@Model
class Trip {
    var name: String
}

func fetch() {
    let container = ModelContainer(for: Trip.self)
    let context = ModelContext(container)
    let fetchDescriptor = FetchDescriptor<Trip>()
    let trips = try! context.fetch(fetchDescriptor)
    
    // Store it somewhere ...
}

How do I observe changes to the trips array? I know that the individual objects inside the array are observable and will change when something is committed to the database. However, what if the order changes, or some trip is deleted or new ones are inserted? I cannot find a mechanism to get notified of this.

The only way I found was using the new @Query property wrapper in SwiftUI. But I want to observe changes outside the SwiftUI environment, in separate classes. Is there a way to do this?

Upvotes: 26

Views: 4496

Answers (2)

Luzo
Luzo

Reputation: 1374

DidSave notification seems to be working, for basic save observation. (stated in comments by hayesk)

Conversion to stream worked for me as well.

NotificationCenter.default.addObserver(
  forName: ModelContext.didSave,
  object: nil,
  queue: .main
) { _ in
  ...
}

or

let stream = AsyncStream { 
  NotificationCenter.default.notifications(named: ModelContext.didSave) 
}

for await _ in stream {
  ...
}

Upvotes: 0

Knotbin
Knotbin

Reputation: 39

This can now be done using the newly announced SwiftData History API (currently in beta for iOS 18).

func fetchTransactions(after tokenData: Data) -> Result<[DefaultHistoryTransaction], Error> {
    do {
        // Decode the given token data.
        let token = try JSONDecoder().decode(History.DefaultToken.self, from: tokenData)
        // Create a history descriptor.
        var descriptor = History.HistoryDescriptor<History.DefaultTransaction>()
        // Fetch the matching history transactions.
        let context = ModelContext(modelContainer)
        let txns = try context.fetchHistory(descriptor)
        return .success(txns)
    } catch {
        return .failure(error)
    }
}

SwiftData History is described in more detail here:

Fetching and filtering time-based model changes | Apple Developer Documentation

Track Model Changes with SwiftData history - WWDC24 - Videos - Apple Developer

Upvotes: 1

Related Questions