SwiftUI View Integration
Patterns for integrating database queries with SwiftUI views and @Observable models.
SwiftUI Views
Direct in View
Use @FetchAll and @FetchOne directly in SwiftUI views:
struct CountersListView: View {
@FetchAll var counters: [Counter]
@FetchOne(Counter.count()) var countersCount = 0
var body: some View {
List {
Text("Total: \(countersCount)")
ForEach(counters) { counter in
Text("\(counter.count)")
}
}
}
}With Query and Animation
struct SwiftUIDemo: View {
@FetchAll(Fact.order { $0.id.desc() }, animation: .default)
private var facts
@FetchOne(Fact.count(), animation: .default)
var factsCount = 0
var body: some View {
List {
Section {
Text("Facts: \(factsCount)")
.font(.largeTitle)
.contentTransition(.numericText(value: Double(factsCount)))
}
Section {
ForEach(facts) { fact in
Text(fact.body)
}
}
}
}
}With Complex Queries
@FetchAll(
RemindersList
.group(by: \.id)
.order(by: \.position)
.leftJoin(Reminder.all) { $0.id.eq($1.remindersListID) && !$1.isCompleted }
.select {
ListSummary.Columns(
list: $0,
incompleteCount: $1.id.count()
)
},
animation: .default
)
var remindersLists@Observable Models
Use @ObservationIgnored to prevent observation of the fetch wrapper itself:
@Observable
@MainActor
class Model {
@ObservationIgnored
@FetchAll(Fact.order { $0.id.desc() }, animation: .default)
var facts
@ObservationIgnored
@FetchOne(Fact.count(), animation: .default)
var factsCount = 0
@ObservationIgnored
@Dependency(\.defaultDatabase) private var database
func deleteFact(indices: IndexSet) {
withErrorReporting {
try database.write { db in
let ids = indices.map { facts[$0].id }
try Fact.where { $0.id.in(ids) }.delete().execute(db)
}
}
}
}In SwiftUI View
struct ObservableModelDemo: View {
@State private var model = Model()
var body: some View {
List {
Text("Facts: \(model.factsCount)")
ForEach(model.facts) { fact in
Text(fact.body)
}
.onDelete { indices in
model.deleteFact(indices: indices)
}
}
}
}Animations
Default Animation
@FetchAll(Counter.all, animation: .default)
var countersCustom Animation
@FetchAll(
Reminder.where { !$0.isCompleted },
animation: .spring(response: 0.3, dampingFraction: 0.7)
)
var incompleteTasksNumeric Transitions
Use .contentTransition() for smooth number updates:
Text("Count: \(factsCount)")
.contentTransition(.numericText(value: Double(factsCount)))Best Practices
- Use
@ObservationIgnoredon@FetchAll/@FetchOnein@Observableclasses - Always specify
animation:parameter for smooth UI updates - Use
.contentTransition()for numeric value animations - Wrap deletes in
withErrorReportingfor consistent error handling - Mark
@Observablemodels as@MainActorwhen used with SwiftUI