@FetchRequest στο SwiftUI — τι είναι, επιλογή Core Data και μηχανισμός

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-06-25 Χρόνος ανάγνωσης: 8 λεπ

@FetchRequest στο SwiftUI — property wrapper για αυτόματη επιλογή δεδομένων από το Core Data με αντιδραστική ενημέρωση της διεπαφής. Το wrapper δέχεται NSFetchRequest με κατηγορήματα φιλτραρίσματος και ταξινομήσεις, και το αποτέλεσμα — ένας πίνακας NSManagedObject — ενημερώνεται αυτόματα σε οποιαδήποτε αλλαγή στο πλαίσιο του Core Data. Σύμφωνα με το Apple Developer Documentation (2025), το @FetchRequest απαιτεί managedObjectContext στο περιβάλλον SwiftUI και υποστηρίζει δυναμικά κατηγορήματα, παρέχοντας αντιδραστική σύνδεση μεταξύ Core Data και διεπαφής χρήστη.

Κύρια σημεία

  • @FetchRequest — property wrapper για αντιδραστική επιλογή Core Data στο SwiftUI
  • Αυτόματη ενημέρωση UI κατά την προσθήκη, διαγραφή ή τροποποίηση εγγραφών στο Core Data
  • Κατηγορήματα και ταξινομήσεις ορίζονται στον αρχικοποιητή ή μέσω παραμέτρων property wrapper
  • managedObjectContext πρέπει να είναι διαθέσιμο μέσω @Environment στην ιεραρχία View
  • Δυναμικά κατηγορήματα επιτρέπουν την αλλαγή συνθηκών φιλτραρίσματος κατά τον χρόνο εκτέλεσης

Τι είναι το @FetchRequest στο SwiftUI;

@FetchRequest — είναι ένα SwiftUI property wrapper που δημιουργεί και διαχειρίζεται ένα FetchRequest από το Core Data. Κατά την αρχικοποίηση, το wrapper δέχεται τον τύπο οντότητας, έναν πίνακα NSSortDescriptor για ταξινόμηση και ένα προαιρετικό NSPredicate για φιλτράρισμα. Το αποτέλεσμα αποθηκεύεται ως πίνακας αντικειμένων του καθορισμένου τύπου NSManagedObject και ενημερώνεται αυτόματα σε αλλαγές στο επίπεδο διατήρησης.

Το βασικό χαρακτηριστικό του @FetchRequest είναι η αντιδραστικότητα. Όταν τα δεδομένα στο Core Data αλλάζουν (save, insert, delete, update), το SwiftUI εκτελεί αυτόματα εκ νέου επιλογή και σχεδιάζει ξανά όλα τα View που χρησιμοποιούν αυτό το fetch request. Ο προγραμματιστής δεν χρειάζεται να καλέσει χειροκίνητα refresh, να εγγραφεί στο NSManagedObjectContextDidSaveNotification ή να επαναφορτώσει δεδομένα. Το SwiftUI διαχειρίζεται ολόκληρο τον κύκλο συγχρονισμού.

Για τη λειτουργία του @FetchRequest απαιτείται NSManagedObjectContext διαθέσιμο στο περιβάλλον SwiftUI μέσω @Environment(\.managedObjectContext). Το πλαίσιο συνήθως μεταβιβάζεται από τη ρίζα της εφαρμογής, όπου δημιουργείται το NSPersistentContainer. Το τυπικό μοτίβο — χρήση ViewModel ή δομής App για τη διαμόρφωση της στοίβας Core Data και μεταβίβαση του πλαισίου μέσω του τροποποιητή .environment().

Ταξινόμηση και κατηγορήματα FetchRequest

Το @FetchRequest δέχεται πολλές παραμέτρους για τη διαμόρφωση της επιλογής. SortDescriptors — ένας πίνακας NSSortDescriptor που καθορίζει τη σειρά των εγγραφών. Predicate — NSPredicate για φιλτράρισμα, για παράδειγμα, επιλογή μόνο εργασιών με κατάσταση “ολοκληρώθηκε”. Animation — κινούμενο σχέδιο για ενημέρωση της λίστας σε αλλαγές δεδομένων.

Τα κατηγορήματα Core Data χρησιμοποιούν μορφή εκφράσεων παρόμοια με SQL: NSPredicate(format: “status == %@”, “completed”). Πολλαπλές συνθήκες μπορούν να συνδυαστούν μέσω AND, OR και NOT. SortDescriptors καθορίζουν τη σειρά: NSSortDescriptor(keyPath: \Task.dueDate, ascending: true). Για ταξινόμηση κατά πολλαπλά πεδία, μεταβιβάζεται ένας πίνακας περιγραφέων — ο πρώτος εφαρμόζεται ως κύριος, οι υπόλοιποι ως δευτερεύοντες.

swift
struct TaskListView: View {
    @Environment(\.managedObjectContext) var viewContext

    @FetchRequest(
        sortDescriptors: [
            NSSortDescriptor(keyPath: \.Task.dueDate, ascending: true),
            NSSortDescriptor(keyPath: \.Task.priority, ascending: false)
        ],
        predicate: NSPredicate(format: "isCompleted == NO"),
        animation: .default
    )
    var tasks: FetchedResults<Task>

    var body: some View {
        List(tasks) { task in
            Text(task.title ?? "")
        }
    }
}

Στο παράδειγμα, το @FetchRequest επιλέγει όλες τις μη ολοκληρωμένες εργασίες (isCompleted == NO), ταξινομημένες πρώτα κατά ημερομηνία (αύξουσα), στη συνέχεια κατά προτεραιότητα (φθίνουσα). FetchedResults<Task> — μια αυτόματα ενημερωμένη συλλογή που συμπεριφέρεται ως πίνακας για ForEach και List. Το κινούμενο σχέδιο .default εξασφαλίζει ομαλή εμφάνιση/εξαφάνιση εγγραφών σε αλλαγές.

@FetchRequest με List και δυναμικά δεδομένα

@FetchRequest συνδυάζεται ιδανικά με List και ForEach για εμφάνιση δυναμικών λιστών Core Data. Το αποτέλεσμα επιλογής (FetchedResults) αντιστοιχεί στο πρωτόκολλο RandomAccessCollection, επομένως μπορεί να χρησιμοποιηθεί απευθείας σε List και ForEach. Κατά την προσθήκη, διαγραφή ή τροποποίηση εγγραφών, η λίστα σχεδιάζεται αυτόματα ξανά με κινούμενο σχέδιο.

Για διαγραφή εγγραφών χρησιμοποιείται η μέθοδος delete του πλαισίου. Για προσθήκη — δημιουργία νέου NSManagedObject και κλήση save. Όλες οι αλλαγές εκτελούνται μέσω viewContext, το οποίο μαζί με το @FetchRequest εξασφαλίζει συνέπεια δεδομένων. Για επεξεργασία χρησιμοποιείται @ObservedObject ή @Bindable για ένα συγκεκριμένο αντικείμενο Core Data.

swift
struct TaskListWithActions: View {
    @Environment(\.managedObjectContext) var viewContext

    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \.Task.createdAt,
                                               ascending: false)]
    )
    var tasks: FetchedResults<Task>

    var body: some View {
        List {
            ForEach(tasks) { task in
                HStack {
                    Text(task.title ?? "")
                    Spacer()
                    Image(systemName: task.isCompleted
                               ? "checkmark.circle.fill"
                               : "circle")
                }
                .onTapGesture { toggleTask(task) }
            }
            .onDelete(perform: deleteTasks)
        }
        .toolbar {
            Button(systemImage: "προσθήκη") { addTask() }
        }
    }

    private func addTask() {
        let newTask = Task(context: viewContext)
        newTask.title = "New task \(tasks.count + 1)"
        newTask.createdAt = Date()
        try? viewContext.save()
    }

    private func toggleTask(_ task: Task) {
        task.isCompleted.toggle()
        try? viewContext.save()
    }

    private func deleteTasks(at offsets: IndexSet) {
        for index in offsets {
            viewContext.delete(tasks[index])
        }
        try? viewContext.save()
    }
}

Αυτό το παράδειγμα παρουσιάζει πλήρες CRUD με @FetchRequest. Προσθήκη: δημιουργία Task μέσω πλαισίου και save(). Διαγραφή: delete() στο πλαίσιο και save(). Ενημέρωση: toggle ιδιότητας και save(). Μετά από κάθε save(), το @FetchRequest ζητά αυτόματα ξανά τα δεδομένα και η List ενημερώνεται με κινούμενο σχέδιο. Οι τροποποιητές onDelete και onTapGesture παρέχουν τυπικές αλληλεπιδράσεις iOS.

Δυναμικά κατηγορήματα κατά τον χρόνο εκτέλεσης

Το @FetchRequest υποστηρίζει δυναμικά κατηγορήματα μέσω της παραμέτρου predicate που μπορεί να αλλάξει κατά τον χρόνο εκτέλεσης. Γι’αυτό, πρέπει να δημιουργήσετε @FetchRequest με μεταβλητή-κατηγόρημα και να το μεταβιβάσετε μέσω Binding. Όταν ο χρήστης αλλάζει το φίλτρο — για παράδειγμα, επιλέγει κατηγορία εργασίας — το κατηγόρημα ενημερώνεται και η επιλογή επανεκκινείται αυτόματα.

Τα δυναμικά κατηγορήματα υλοποιούνται μέσω ιδιότητας @State ή @Published, η οποία μεταβιβάζεται ως Binding στον αρχικοποιητή του @FetchRequest. Το SwiftUI παρακολουθεί την αλλαγή αυτού του Binding και αναδημιουργεί το NSFetchRequest με το νέο κατηγόρημα, ενημερώνοντας αυτόματα τη λίστα.

Παραδείγματα κώδικα @FetchRequest

Ας εξετάσουμε ένα πλήρες παράδειγμα εφαρμογής ημερολογίου με @FetchRequest για επιλογή εγγραφών. Η φόρμα αναζήτησης χρησιμοποιεί δυναμικό κατηγόρημα: κατά την εισαγωγή κειμένου, το FetchRequest ενημερώνει αυτόματα τα αποτελέσματα, φιλτράροντας εγγραφές κατά τίτλο και περιεχόμενο. Ταξινόμηση — κατά ημερομηνία δημιουργίας, από νέο προς παλιό.

swift
struct JournalEntryView: View {
    @Environment(\.managedObjectContext) var viewContext
    @State private var searchText = ""

    private var searchPredicate: NSPredicate? {
        guard !searchText.isEmpty else { return nil }
        return NSPredicate(format: "title CONTAINS[c] %@", searchText)
    }

    @FetchRequest var entries: FetchedResults<JournalEntry>

    init(searchText: String) {
        let sort = [NSSortDescriptor(keyPath: \.JournalEntry.date,
                                           ascending: false)]
        let pred: NSPredicate? = searchText.isEmpty
            ? nil
            : NSPredicate(format: "title CONTAINS[c] %@ OR content CONTAINS[c] %@",
                          searchText, searchText)
        self._entries = FetchRequest(
            sortDescriptors: sort,
            predicate: pred,
            animation: .default
        )
    }

    var body: some View {
        List(entries) { entry in
            VStack(alignment: .leading) {
                Text(entry.title ?? "").font(.headline)
                Text(entry.content ?? "").font(.subheadline)
                    .lineLimit(2)
            }
        }
        .searchable(text: $searchText)
    }
}

Ημερολόγιο εγγραφών με αναζήτηση: κατά την εισαγωγή κειμένου στο πεδίο searchable, δημιουργείται NSPredicate με CONTAINS[c] (αναζήτηση χωρίς διάκριση πεζών/κεφαλαίων) στα πεδία title και content. Το Init δέχεται searchText και δημιουργεί FetchRequest με το αντίστοιχο κατηγόρημα. Ο τροποποιητής Searchable ενημερώνει αυτόματα το searchText, και το @FetchRequest αντιδρά στην αλλαγή του κατηγορήματος και ζητά ξανά τα δεδομένα.

Για πιο περίπλοκα σενάρια με πολλαπλά φίλτρα, συνδυάστε το NSCompoundPredicate με λογική AND/OR. Το @FetchRequest υποστηρίζει κατηγορήματα οποιασδήποτε πολυπλοκότητας, συμπεριλαμβανομένων ένθετων υποερωτημάτων (SUBQUERY) για φιλτράρισμα κατά σχέσεις. Ωστόσο, να θυμάστε: τα περίπλοκα κατηγορήματα επηρεάζουν την απόδοση — για μεγάλα σύνολα δεδομένων (πάνω από 10.000 εγγραφές) χρησιμοποιήστε ευρετηρίαση Core Data και περιορίστε την επιλογή μέσω fetchLimit.

Συχνές ερωτήσεις

Πώς μεταβιβάζεται το managedObjectContext στο @FetchRequest;

Προσθέστε @Environment(\.managedObjectContext) var viewContext στο ριζικό View και μεταβιβάστε το μέσω του τροποποιητή .environment(\.managedObjectContext, context). Όλα τα θυγατρικά View που χρησιμοποιούν @FetchRequest θα λάβουν αυτόματα το πλαίσιο από το περιβάλλον.

Μπορεί το @FetchRequest να ενημερωθεί χωρίς επαναφόρτωση του View;

Ναι, τροποποιήστε το πλαίσιο Core Data: προσθέστε, διαγράψτε ή ενημερώστε το αντικείμενο και καλέστε save(). Το @FetchRequest θα εκτελέσει αυτόματα εκ νέου επιλογή και θα ενημερώσει το UI. Για εξαναγκασμένη ενημέρωση χωρίς save() χρησιμοποιήστε το viewContext.refreshAllObjects().

Ποια είναι η διαφορά μεταξύ @FetchRequest και @SectionedFetchRequest;

Το @FetchRequest επιστρέφει έναν επίπεδο πίνακα αντικειμένων. Το @SectionedFetchRequest ομαδοποιεί τα αποτελέσματα κατά το καθορισμένο key path σε ενότητες, παρόμοια με το UITableView με sectionNameKeyPath. Χρησιμοποιήστε @SectionedFetchRequest για ομαδοποιημένες λίστες με επικεφαλίδες ενοτήτων.

Πώς χρησιμοποιώ το @FetchRequest με Relationship;

Για φιλτράρισμα κατά σχέσεις χρησιμοποιήστε σημειογραφία με τελεία: NSPredicate(format: “category.name == %@”, “Work”). Το @FetchRequest εκτελεί αυτόματα JOIN στην SQLite. Για πρόσβαση στη σχέση μέσω του επιλεγμένου αντικειμένου χρησιμοποιήστε συνήθεις ιδιότητες NSManagedObject.

Υποστηρίζει το @FetchRequest σελιδοποίηση;

Το @FetchRequest δεν υποστηρίζει άμεσα σελιδοποίηση, καθώς έχει σχεδιαστεί για εμφάνιση όλων των εγγραφών με αντιδραστική ενημέρωση. Για σελιδοποίηση χρησιμοποιήστε NSFetchRequest με fetchLimit/fetchOffset και φορτώστε δεδομένα χειροκίνητα κατά την κύλιση της λίστας.

Περίληψη

  • @FetchRequest — property wrapper για αυτόματη αντιδραστική επιλογή Core Data στο SwiftUI
  • Ταξινόμηση ορίζεται με πίνακα NSSortDescriptor, φιλτράρισμα — με NSPredicate
  • Αυτόματη ενημέρωση UI σε οποιαδήποτε αλλαγή δεδομένων στο πλαίσιο Core Data
  • managedObjectContext μεταβιβάζεται μέσω @Environment στην ιεραρχία View
  • Δυναμικά κατηγορήματα υλοποιούνται μέσω custom init με παράμετρο κατηγορήματος
  • Λειτουργίες CRUD εκτελούνται μέσω viewContext με αυτόματο συγχρονισμό λίστας
  • Χρησιμοποιήστε @FetchRequest για εμφάνιση Core Data, @SectionedFetchRequest — για ομαδοποιημένες λίστες

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης