In SwiftData, handling data retrieval effectively is essential for creating smooth and responsive apps. FetchPredicate
and SortDescriptor
are two powerful tools that allow you to filter and organize your data efficiently. This article explores how to use FetchPredicate
and SortDescriptor
with SwiftData to fine-tune your data-fetching capabilities.
Using Predicate and SortDescriptor
Given our data model Task
:
import SwiftData
@Model
class Task: Identifiable {
var id: String {
title
}
var title: String
var isCompleted: Bool
init(title: String, isCompleted: Bool = false) {
self.title = title
self.isCompleted = isCompleted
}
}
We can filter our data by adding a predicate to our @Query
property. At the same time, we can add some SortDescriptor
to sort that result:
import SwiftData
struct ContentView: View {
@Query(
filter: #Predicate { $0.isCompleted },
sort: [SortDescriptor(\.title)]
) var completedTasks: [Task]
var body: some View {
List(completedTasks) { task in
Text(task.title)
}
}
}
Fetching data from the ViewModel
We can move the fetching logic to our ViewModel with a couple of steps:
-
Inject the
ModelContext
through the View'sinit
, this allows us to inject it in our ViewModel. -
To be able to do this we need to initialise the
ModelContext
container in the main App file, and then inject it to our view.
This is the final result (keeping the model as it is):
Create a ViewModel that will take ModelContext
, which will be used to fetch the data.
import SwiftUI
import SwiftData
@Observable
class ViewModel {
var modelContext: ModelContext
var completedTasks: [Task] = []
init(modelContext: ModelContext) {
self.modelContext = modelContext
}
var fetchDescriptor = FetchDescriptor(
predicate: #Predicate { task in
return task.isCompleted
},
sortBy: [
SortDescriptor(\.title)
]
)
func onAppear() {
do {
completedTasks = try modelContext.fetch(fetchDescriptor)
}
catch {
print("Error occured")
}
}
}
The view will get the ModelContext
in the init
and instantiate the ViewModel with it.
import SwiftUI
import SwiftData
struct ContentView: View {
@State var viewModel: ViewModel
init(modelContext: ModelContext) {
self.viewModel = ViewModel(modelContext: modelContext)
}
var body: some View {
List(viewModel.completedTasks) { task in
Text(task.title)
}
.onAppear {
viewModel.onAppear()
}
}
}
Finally, our main App file will create and inject the ModelContext
.
import SwiftUI
import SwiftData
@main
struct EducaSwiftApp: App {
let container: ModelContainer
var body: some Scene {
WindowGroup {
ContentView(modelContext: container.mainContext)
}
}
init() {
do {
container = try ModelContainer(for: Task.self)
}
catch {
fatalError("Error creating Task model context")
}
}
}
Be the first to comment