HomeiOS Developmentios - How do I refresh a SwiftData (guide) fetch at any...

ios – How do I refresh a SwiftData (guide) fetch at any time when the database is modified?


Context

I am fetching numerous objects from a SwiftData retailer utilizing a frequently-changing predicate. Conventional @Question setups didn’t present the pliability I needed (particularly for rendering loading states), so I created a background actor to deal with fetching the info:

@ModelActor
actor ThreadsafeBackgroundActor: Sendable {
    func fetchData(_ predicate: Predicate? = nil) throws -> [CardView] {
        let descriptor = if let p = predicate {
            FetchDescriptor(predicate: p)
        } else {
            FetchDescriptor()
        }
        let playing cards = attempt context.fetch(descriptor)
        return playing cards.map(CardView.init)
    }
}

I’ve additionally acquired a view mannequin calling the actor:

@Observable
class CardListViewModel {
    enum State {
        case idle
        case loading
        case failed(Error)
        case loaded([CardView])
    }

    personal(set) var state = State.idle

    func fetchData(container: ModelContainer, predicate: Predicate) async throws -> [CardView] {
        let service = ThreadsafeBackgroundActor(modelContainer: container)
        return attempt await service.fetchData(predicate)
    }

    @MainActor func load(container: ModelContainer, filter: CardPredicate) async {
        state = .loading

        do {
            let playing cards = attempt await fetchData(container: container, predicate: filter.predicate)
            state = .loaded(playing cards)
        } catch is CancellationError {
            state = .idle
        } catch {
            state = .failed(error)
        }
    }
}

And I’ve acquired a process on my SwiftUI view to kick off the preliminary load:

.process(id: cardFilter) { // Reloads at any time when the cardboard filter modifications! Good!
    viewModel.load(container: context.container, filter: cardFilter)
}

This setup works excellently till something within the database modifications. Database updates (inserts, modifications, deletes) don’t set off my load operate, even after calling context.save().

How can I make my load operate re-run at any time when the database modifications?

Makes an attempt and Analysis

  • I’ve tried a brute-force route of introducing a reloadCount state variable, handed round wherever wanted. Areas of the code that replace the database increment reloadCount, and a separate process in my record view watches the rely:

    .process(id: cardFilter) { /* Similar name */ }
    .process(id: reloadCount) { // Eww...
         viewModel.load(container: context.container, filter: cardFilter)
    }
    

    Not solely is that this technique tedious and brittle, however it additionally runs the danger of calling load a number of occasions unnecessarily (particularly on the preliminary render time).

  • I’ve seemed into Swift’s streaming notification system. I am pretty assured that NSPersistentStoreRemoteChange is what I need to watch. I simply can not determine how/the place to initialize that watcher. addObserver asks for Goal-C annotations. I do not suppose that .writer().sink { } is the answer both, as a result of I need to kick off the mutating name viewModel.load() within the (escaping) closure.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments