I need a way to keep a global count of all model items in SwiftData.
My goal is to:
- track how many entries exist in the model.
- have the count be reactive (update when items are inserted or deleted).
- handle a lot of pre-existing records.
This is for an internal app with thousands of records already, and potentially up to 50k after bulk imports.
I know there are other platforms, I want to keep this conversation about SwiftData though.
What I’ve tried:
@Query
in.environment
- Works, but it loads all entries in memory just to get a count.
- Not scalable with tens of thousands of records.
modelContext.fetchCount
- Efficient, but only runs once.
- Not reactive, would need to be recalled every time
NotificationCenter in
@Observable
Tried observing context changes, but couldn’t get fetchCount to update reactively.
@Observable final class EntityCountManager { private(set) var total: Int private var context: ModelContext private var cancellable: AnyCancellable? init(context: ModelContext) { self.total = 0 self.context = context self.cancellable = NotificationCenter.default .publisher(for: .NSManagedObjectContextObjectsDidChange, object: context) .sink { [weak self] _ in self?.refresh() } self.refresh() } private func refresh() { let descriptor = FetchDescriptor<Intake>() self.total = (try? context.fetchCount(descriptor)) ?? 0 } }
Custom Property Wrapper
This works like
@Query
, but still loads everything in memory.E.g.:
@propertyWrapper struct ItemCount<T: PersistentModel>: DynamicProperty { @Environment(\.modelContext) private var context @Query private var results: [T] var wrappedValue: Int { results.count } init(filter: Predicate<T>? = nil, sort: [SortDescriptor<T>] = []) { _results = Query(filter: filter, sort: sort) } }
What I want:
- A way to get
.fetchCount
to work reactively with insertions/deletions. - Or some observable model I can use as a single source of truth, so the count and derived calculations are accessible across multiple screens, without duplicating
@Query
everywhere.
Is there a SwiftData way to maintain a reactive count of items without loading all the models into memory every time I need it?
NotificationCenter
approach? Every time you receive the notification, just callfetchCount
, right? What about that is not reactive?.NSManagedObjectContextDidSave
but then changed it to. NSManagedObjectContextObjectsDidChange
ModelContext
is a red flag to me. Did you initialise theState
ininit
of the view?self._manager = State(wrappedValue: EntityCountManager(modelContext))
will not work because the@Environment
has not been initialised at that time.ModelContext.didSave
instead? You don't even have access to aNSManagedObjectContext
instance.