@FetchRequest στο SwiftUI — property wrapper για αυτόματη επιλογή δεδομένων από το Core Data με αντιδραστική ενημέρωση της διεπαφής. Το wrapper δέχεται NSFetchRequest με κατηγορήματα φιλτραρίσματος και ταξινομήσεις, και το αποτέλεσμα — ένας πίνακας NSManagedObject — ενημερώνεται αυτόματα σε οποιαδήποτε αλλαγή στο πλαίσιο του Core Data. Σύμφωνα με το Apple Developer Documentation (2025), το @FetchRequest απαιτεί managedObjectContext στο περιβάλλον SwiftUI και υποστηρίζει δυναμικά κατηγορήματα, παρέχοντας αντιδραστική σύνδεση μεταξύ Core Data και διεπαφής χρήστη.
Κύρια σημεία
@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 δέχεται πολλές παραμέτρους για τη διαμόρφωση της επιλογής. SortDescriptors — ένας πίνακας NSSortDescriptor που καθορίζει τη σειρά των εγγραφών. Predicate — NSPredicate για φιλτράρισμα, για παράδειγμα, επιλογή μόνο εργασιών με κατάσταση “ολοκληρώθηκε”. Animation — κινούμενο σχέδιο για ενημέρωση της λίστας σε αλλαγές δεδομένων.
Τα κατηγορήματα Core Data χρησιμοποιούν μορφή εκφράσεων παρόμοια με SQL: NSPredicate(format: “status == %@”, “completed”). Πολλαπλές συνθήκες μπορούν να συνδυαστούν μέσω AND, OR και NOT. SortDescriptors καθορίζουν τη σειρά: NSSortDescriptor(keyPath: \Task.dueDate, ascending: true). Για ταξινόμηση κατά πολλαπλά πεδία, μεταβιβάζεται ένας πίνακας περιγραφέων — ο πρώτος εφαρμόζεται ως κύριος, οι υπόλοιποι ως δευτερεύοντες.
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 και ForEach για εμφάνιση δυναμικών λιστών Core Data. Το αποτέλεσμα επιλογής (FetchedResults) αντιστοιχεί στο πρωτόκολλο RandomAccessCollection, επομένως μπορεί να χρησιμοποιηθεί απευθείας σε List και ForEach. Κατά την προσθήκη, διαγραφή ή τροποποίηση εγγραφών, η λίστα σχεδιάζεται αυτόματα ξανά με κινούμενο σχέδιο.
Για διαγραφή εγγραφών χρησιμοποιείται η μέθοδος delete του πλαισίου. Για προσθήκη — δημιουργία νέου NSManagedObject και κλήση save. Όλες οι αλλαγές εκτελούνται μέσω viewContext, το οποίο μαζί με το @FetchRequest εξασφαλίζει συνέπεια δεδομένων. Για επεξεργασία χρησιμοποιείται @ObservedObject ή @Bindable για ένα συγκεκριμένο αντικείμενο Core Data.
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 ενημερώνει αυτόματα τα αποτελέσματα, φιλτράροντας εγγραφές κατά τίτλο και περιεχόμενο. Ταξινόμηση — κατά ημερομηνία δημιουργίας, από νέο προς παλιό.
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.
Συχνές ερωτήσεις
Προσθέστε @Environment(\.managedObjectContext) var viewContext στο ριζικό View και μεταβιβάστε το μέσω του τροποποιητή .environment(\.managedObjectContext, context). Όλα τα θυγατρικά View που χρησιμοποιούν @FetchRequest θα λάβουν αυτόματα το πλαίσιο από το περιβάλλον.
Ναι, τροποποιήστε το πλαίσιο Core Data: προσθέστε, διαγράψτε ή ενημερώστε το αντικείμενο και καλέστε save(). Το @FetchRequest θα εκτελέσει αυτόματα εκ νέου επιλογή και θα ενημερώσει το UI. Για εξαναγκασμένη ενημέρωση χωρίς save() χρησιμοποιήστε το viewContext.refreshAllObjects().
Το @FetchRequest επιστρέφει έναν επίπεδο πίνακα αντικειμένων. Το @SectionedFetchRequest ομαδοποιεί τα αποτελέσματα κατά το καθορισμένο key path σε ενότητες, παρόμοια με το UITableView με sectionNameKeyPath. Χρησιμοποιήστε @SectionedFetchRequest για ομαδοποιημένες λίστες με επικεφαλίδες ενοτήτων.
Για φιλτράρισμα κατά σχέσεις χρησιμοποιήστε σημειογραφία με τελεία: NSPredicate(format: “category.name == %@”, “Work”). Το @FetchRequest εκτελεί αυτόματα JOIN στην SQLite. Για πρόσβαση στη σχέση μέσω του επιλεγμένου αντικειμένου χρησιμοποιήστε συνήθεις ιδιότητες NSManagedObject.
Το @FetchRequest δεν υποστηρίζει άμεσα σελιδοποίηση, καθώς έχει σχεδιαστεί για εμφάνιση όλων των εγγραφών με αντιδραστική ενημέρωση. Για σελιδοποίηση χρησιμοποιήστε NSFetchRequest με fetchLimit/fetchOffset και φορτώστε δεδομένα χειροκίνητα κατά την κύλιση της λίστας.
Περίληψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης