@FetchRequest în SwiftUI — ce este, selectarea Core Data și mecanismul

Autor: IT Sectr Publicat: 2026-06-25 Timp de citire: 8 min

@FetchRequest în SwiftUI — property wrapper pentru selectarea automată a datelor din Core Data cu actualizarea reactivă a interfeței. Wrapper primește NSFetchRequest cu predicate de filtrare și sortări, iar rezultatul — un array NSManagedObject — se actualizează automat la orice modificare în contextul Core Data. Conform Apple Developer Documentation (2025), @FetchRequest necesită managedObjectContext în mediul SwiftUI și suportă predicate dinamice, asigurând o legătură reactivă între Core Data și interfața utilizatorului.

Principalele puncte

  • @FetchRequest — property wrapper pentru selectarea reactivă a Core Data în SwiftUI
  • Actualizare automată UI la adăugarea, ștergerea sau modificarea înregistrărilor în Core Data
  • Predicatele și sortările se setează în inițializator sau prin parametrii property wrapper
  • managedObjectContext trebuie să fie disponibil prin @Environment în ierarhia View
  • Predicate dinamice permit schimbarea condițiilor de filtrare în timpul execuției

Ce este @FetchRequest în SwiftUI?

@FetchRequest — este un property wrapper SwiftUI care creează și gestionează un FetchRequest din Core Data. La inițializare, wrapper primește tipul entității, un array NSSortDescriptor pentru sortare și un NSPredicate opțional pentru filtrare. Rezultatul este stocat ca un array de obiecte de tipul NSManagedObject specificat și se actualizează automat la modificări în stratul de persistență.

Caracteristica cheie a @FetchRequest este reactivitatea. Când datele din Core Data se modifică (save, insert, delete, update), SwiftUI execută automat o reselectare și redesenează toate View-urile care folosesc acest fetch request. Dezvoltatorul nu trebuie să apeleze manual refresh, să se aboneze la NSManagedObjectContextDidSaveNotification sau să reîncarce datele. SwiftUI gestionează întregul ciclu de sincronizare.

Pentru funcționare, @FetchRequest necesită NSManagedObjectContext disponibil în mediul SwiftUI prin @Environment(\.managedObjectContext). Contextul este de obicei transmis din rădăcina aplicației, unde este creat NSPersistentContainer. Modelul standard — utilizarea ViewModel sau a structurii App pentru configurarea stivei Core Data și transmiterea contextului prin modificatorul .environment().

Sortarea și predicatele FetchRequest

@FetchRequest primește mai mulți parametri pentru configurarea selectării. SortDescriptors — un array NSSortDescriptor care determină ordinea înregistrărilor. Predicate — NSPredicate pentru filtrare, de exemplu, selectarea doar a sarcinilor cu statusul „finalizat". Animation — animație pentru actualizarea listei la modificări ale datelor.

Predicatele Core Data folosesc un format de expresii asemănător SQL: NSPredicate(format: „status == %@", „completed"). Se pot combina mai multe condiții prin AND, OR și NOT. SortDescriptors stabilesc ordinea: NSSortDescriptor(keyPath: \Task.dueDate, ascending: true). Pentru sortarea după mai multe câmpuri se transmite un array de descriptori — primul se aplică ca principal, următoarele ca secundare.

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 ?? "")
        }
    }
}

În exemplu, @FetchRequest selectează toate sarcinile nefinalizate (isCompleted == NO), sortate mai întâi după dată (crescător), apoi după prioritate (descrescător). FetchedResults<Task> — este o colecție actualizată automat care se comportă ca un array pentru ForEach și List. Animația .default asigură apariția/dispariția lină a înregistrărilor la modificări.

@FetchRequest cu List și date dinamice

@FetchRequest se combină ideal cu List și ForEach pentru afișarea listelor dinamice Core Data. Rezultatul selectării (FetchedResults) corespunde protocolului RandomAccessCollection, deci poate fi folosit direct în List și ForEach. La adăugarea, ștergerea sau modificarea înregistrărilor, lista se redesenează automat cu animație.

Pentru ștergerea înregistrărilor se folosește metoda delete a contextului. Pentru adăugare — crearea unui nou NSManagedObject și apelarea save. Toate modificările se execută prin viewContext, care împreună cu @FetchRequest asigură consistența datelor. Pentru editare se folosește @ObservedObject sau @Bindable pentru un obiect Core Data specific.

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: "adaugă") { 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()
    }
}

Acest exemplu demonstrează CRUD complet cu @FetchRequest. Adăugare: crearea Task prin context și save(). Ștergere: delete() pe context și save(). Actualizare: toggle proprietății și save(). După fiecare save(), @FetchRequest reinteroghează automat datele, iar List se actualizează cu animație. Modificatorii onDelete și onTapGesture asigură interacțiunile standard iOS.

Predicate dinamice în timpul execuției

@FetchRequest suportă predicate dinamice prin parametrul predicate care poate fi modificat în timpul execuției. Pentru aceasta trebuie creat @FetchRequest cu o variabilă-predicat și transmisă prin Binding. Când utilizatorul schimbă filtrul — de exemplu, selectează o categorie de sarcini — predicatul se actualizează, iar selectarea se repornește automat.

Predicatele dinamice se implementează prin proprietatea @State sau @Published, care este transmisă ca Binding la inițializatorul @FetchRequest. SwiftUI urmărește modificarea acestui Binding și recrează NSFetchRequest cu noul predicat, actualizând automat lista.

Exemple de cod @FetchRequest

Să analizăm un exemplu complet de aplicație jurnal cu @FetchRequest pentru selectarea înregistrărilor. Formularul de căutare folosește un predicat dinamic: la introducerea textului, FetchRequest actualizează automat rezultatele, filtrând înregistrările după titlu și conținut. Sortare — după data creării, de la cele mai noi la cele mai vechi.

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)
    }
}

Jurnal de înregistrări cu căutare: la introducerea textului în câmpul searchable, se creează NSPredicate cu CONTAINS[c] (căutare fără sensibilitate la majuscule) în câmpurile title și content. Init primește searchText și creează FetchRequest cu predicatul corespunzător. Modificatorul Searchable actualizează automat searchText, iar @FetchRequest reacționează la modificarea predicatului și reinteroghează datele.

Pentru scenarii mai complexe cu filtre multiple, combinați NSCompoundPredicate cu logica AND/OR. @FetchRequest suportă predicate de orice complexitate, inclusiv subinterogări imbricate (SUBQUERY) pentru filtrarea după relații. Totuși, rețineți: predicatele complexe afectează performanța — pentru seturi mari de date (peste 10 000 de înregistrări) utilizați indexarea Core Data și limitați selectarea prin fetchLimit.

Întrebări frecvente

Cum se transmite managedObjectContext la @FetchRequest?

Adăugați @Environment(\.managedObjectContext) var viewContext în View-ul rădăcină și transmiteți-l prin modificatorul .environment(\.managedObjectContext, context). Toate View-urile copil care folosesc @FetchRequest vor primi automat contextul din mediu.

Se poate actualiza @FetchRequest fără reîncărcarea View-ului?

Da, modificați contextul Core Data: adăugați, ștergeți sau actualizați obiectul și apelați save(). @FetchRequest va executa automat o reselectare și va actualiza UI. Pentru actualizare forțată fără save() utilizați viewContext.refreshAllObjects().

Care este diferența între @FetchRequest și @SectionedFetchRequest?

@FetchRequest returnează un array plat de obiecte. @SectionedFetchRequest grupează rezultatele după key path-ul specificat în secțiuni, similar cu UITableView cu sectionNameKeyPath. Utilizați @SectionedFetchRequest pentru liste grupate cu titluri de secțiuni.

Cum se folosește @FetchRequest cu Relationship?

Pentru filtrarea după relații utilizați notația cu punct: NSPredicate(format: „category.name == %@", „Work"). @FetchRequest execută automat JOIN în SQLite. Pentru accesul la relație prin obiectul selectat, utilizați proprietățile obișnuite NSManagedObject.

Suportă @FetchRequest paginarea?

@FetchRequest nu suportă paginarea direct, deoarece este destinat afișării tuturor înregistrărilor cu actualizare reactivă. Pentru paginare utilizați NSFetchRequest cu fetchLimit/fetchOffset și încărcați datele manual la derularea listei.

Rezumat

  • @FetchRequest — property wrapper pentru selectarea automată reactivă a Core Data în SwiftUI
  • Sortarea se setează cu array NSSortDescriptor, filtrarea — cu NSPredicate
  • Actualizare automată UI la orice modificare a datelor în contextul Core Data
  • managedObjectContext se transmite prin @Environment în ierarhia View
  • Predicate dinamice se implementează prin init custom cu parametru predicat
  • Operații CRUD se execută prin viewContext cu sincronizare automată a listei
  • Utilizați @FetchRequest pentru afișarea Core Data, @SectionedFetchRequest — pentru liste grupate

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și