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

Разговарајте о пројекту

Прочитајте такође