@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. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође