@FetchRequest ve SwiftUI — property wrapper pro automatický výběr dat z Core Data s reaktivní aktualizací rozhraní. Wrapper přijímá NSFetchRequest s predikáty filtrování a řazením, a výsledek — pole NSManagedObject — se automaticky aktualizuje při jakékoli změně v kontextu Core Data. Podle Apple Developer Documentation (2025) vyžaduje @FetchRequest managedObjectContext v prostředí SwiftUI a podporuje dynamické predikáty, což zajišťuje reaktivní propojení mezi Core Data a uživatelským rozhraním.
Hlavní body
@FetchRequest — je property wrapper SwiftUI, který vytváří a spravuje FetchRequest z Core Data. Při inicializaci wrapper přijímá typ entity, pole NSSortDescriptor pro řazení a volitelný NSPredicate pro filtrování. Výsledek je uložen jako pole objektů zadaného typu NSManagedObject a automaticky se aktualizuje při změnách v perzistenční vrstvě.
Klíčovou vlastností @FetchRequest je reaktivita. Když se data v Core Data změní (save, insert, delete, update), SwiftUI automaticky provede opětovný výběr a překreslí všechna View používající tento fetch request. Vývojář nemusí ručně volat refresh, odebírat se na NSManagedObjectContextDidSaveNotification nebo znovu načítat data. SwiftUI řídí celý cyklus synchronizace.
Pro fungování @FetchRequest je vyžadován NSManagedObjectContext dostupný v prostředí SwiftUI přes @Environment(\.managedObjectContext). Kontext je obvykle předáván z kořene aplikace, kde je vytvořen NSPersistentContainer. Standardní vzor — použití ViewModel nebo struktury App pro konfiguraci Core Data stacku a předání kontextu přes modifikátor .environment().
@FetchRequest přijímá několik parametrů pro konfiguraci výběru. SortDescriptors — pole NSSortDescriptor určující pořadí záznamů. Predicate — NSPredicate pro filtrování, například výběr pouze úkolů se stavem „dokončeno”. Animation — animace pro aktualizaci seznamu při změnách dat.
Predikáty Core Data používají formát výrazů podobný SQL: NSPredicate(format: „status == %@”, „completed”). Více podmínek lze kombinovat pomocí AND, OR a NOT. SortDescriptors určují pořadí: NSSortDescriptor(keyPath: \Task.dueDate, ascending: true). Pro řazení podle více polí se předává pole deskriptorů — první se aplikuje jako hlavní, zbývající jako sekundární.
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 ?? "")
}
}
}
V příkladu @FetchRequest vybírá všechny nedokončené úkoly (isCompleted == NO), seřazené nejprve podle data (vzestupně), poté podle priority (sestupně). FetchedResults<Task> — automaticky aktualizovaná kolekce, která se chová jako pole pro ForEach a List. Animace .default zajišťuje plynulé zobrazování/mizení záznamů při změnách.
@FetchRequest se ideálně kombinuje s List a ForEach pro zobrazení dynamických seznamů Core Data. Výsledek výběru (FetchedResults) odpovídá protokolu RandomAccessCollection, takže jej lze použít přímo v List a ForEach. Při přidávání, mazání nebo změně záznamů se seznam automaticky překresluje s animací.
Pro mazání záznamů se používá metoda kontextu delete. Pro přidávání — vytvoření nového NSManagedObject a volání save. Všechny změny se provádějí přes viewContext, který spolu s @FetchRequest zajišťuje konzistenci dat. Pro úpravy se používá @ObservedObject nebo @Bindable pro konkrétní objekt 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: "přidat") { 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()
}
}
Tento příklad demonstruje plné CRUD s @FetchRequest. Přidání: vytvoření Task přes kontext a save(). Mazání: delete() na kontextu a save(). Aktualizace: toggle vlastnosti a save(). Po každém save() @FetchRequest automaticky znovu vyžádá data a List se animovaně aktualizuje. Modifikátory onDelete a onTapGesture poskytují standardní iOS interakce.
@FetchRequest podporuje dynamické predikáty přes parametr predicate, který lze měnit za běhu. K tomu je třeba vytvořit @FetchRequest s proměnnou-predikátem a předat ji přes Binding. Když uživatel změní filtr — například vybere kategorii úkolů — predikát se aktualizuje a výběr se automaticky restartuje.
Dynamické predikáty se implementují přes vlastnost @State nebo @Published, která se předává jako Binding do inicializátoru @FetchRequest. SwiftUI sleduje změnu tohoto Binding a znovu vytváří NSFetchRequest s novým predikátem, automaticky aktualizuje seznam.
Podívejme se na úplný příklad aplikace deníku s @FetchRequest pro výběr záznamů. Vyhledávací formulář používá dynamický predikát: při zadávání textu FetchRequest automaticky aktualizuje výsledky a filtruje záznamy podle názvu a obsahu. Řazení — podle data vytvoření, od nejnovějších po nejstarší.
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)
}
}
Denník záznamů s vyhledáváním: při zadávání textu do searchable pole se vytvoří NSPredicate s CONTAINS[c] (vyhledávání bez rozlišení velikosti písmen) v polích title a content. Init přijímá searchText a vytváří FetchRequest s odpovídajícím predikátem. Modifikátor Searchable automaticky aktualizuje searchText a @FetchRequest reaguje na změnu predikátu a znovu vyžádá data.
Pro složitější scénáře s více filtry kombinujte NSCompoundPredicate s logikou AND/OR. @FetchRequest podporuje predikáty libovolné složitosti, včetně vnořených poddotazů (SUBQUERY) pro filtrování podle relací. Nicméně pamatujte: složité predikáty ovlivňují výkon — pro velké datové sady (více než 10 000 záznamů) použijte indexování Core Data a omezte výběr přes fetchLimit.
Často kladené otázky
Přidejte @Environment(\.managedObjectContext) var viewContext do kořenového View a předejte jej přes modifikátor .environment(\.managedObjectContext, context). Všechna podřízená View používající @FetchRequest automaticky získají kontext z prostředí.
Ano, změňte kontext Core Data: přidejte, smažte nebo aktualizujte objekt a zavolejte save(). @FetchRequest automaticky provede opětovný výběr a aktualizuje UI. Pro vynucenou aktualizaci bez save() použijte viewContext.refreshAllObjects().
@FetchRequest vrací ploché pole objektů. @SectionedFetchRequest seskupuje výsledky podle zadané key path do sekcí, podobně jako UITableView s sectionNameKeyPath. Použijte @SectionedFetchRequest pro seskupené seznamy s nadpisy sekcí.
Pro filtrování podle relací použijte tečkovou notaci: NSPredicate(format: „category.name == %@”, „Work”). @FetchRequest automaticky provádí JOIN v SQLite. Pro přístup k relaci přes vybraný objekt použijte běžné vlastnosti NSManagedObject.
@FetchRequest přímo nepodporuje stránkování, protože je určen k zobrazení všech záznamů s reaktivní aktualizací. Pro stránkování použijte NSFetchRequest s fetchLimit/fetchOffset a načítejte data ručně při posouvání seznamu.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také