SwiftData II
Predicate & SortDescriptor
SwiftData II
Predicate & SortDescriptor
0
0
Checkbox to mark video as read
Mark as read

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's init, 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")
        }
    }
}

0 Comments

Join the community to comment

Be the first to comment

Accept Cookies

We use cookies to collect and analyze information on site performance and usage, in order to provide you with better service.

Check our Privacy Policy