@FetchRequest in SwiftUI — cos'è, recupero di Core Data e meccanismo

Autore: IT Sectr Pubblicato: 2026-06-25 Tempo di lettura: 8 min

@FetchRequest in SwiftUI — un property wrapper per il recupero automatico di dati da Core Data con aggiornamento reattivo dell'interfaccia. Il wrapper accetta NSFetchRequest con predicati di filtro e ordinamenti, e il risultato — un array di NSManagedObject — viene automaticamente aggiornato a qualsiasi modifica nel contesto di Core Data. Secondo la Apple Developer Documentation (2025), @FetchRequest richiede managedObjectContext nell'ambiente SwiftUI e supporta predicati dinamici, fornendo una connessione reattiva tra Core Data e l'interfaccia utente.

Punti chiave

  • @FetchRequest — un property wrapper per il recupero reattivo di Core Data in SwiftUI
  • Aggiornamento automatico dell'interfaccia quando si aggiungono, eliminano o modificano record in Core Data
  • Predicati e ordinamenti impostati nell'inizializzatore o tramite parametri del property wrapper
  • managedObjectContext deve essere disponibile tramite @Environment nella gerarchia delle View
  • Predicati dinamici permettono di cambiare le condizioni di filtro in fase di esecuzione

Cos'è @FetchRequest in SwiftUI?

@FetchRequest è un property wrapper di SwiftUI che crea e gestisce un FetchRequest da Core Data. All'inizializzazione, il wrapper accetta un tipo di entità, un array di NSSortDescriptor per l'ordinamento e un NSPredicate opzionale per il filtraggio. Il risultato viene memorizzato come un array di oggetti del tipo NSManagedObject specificato e si aggiorna automaticamente quando si verificano modifiche nel livello di persistenza.

La caratteristica principale di @FetchRequest è la reattività. Quando i dati in Core Data cambiano (save, insert, delete, update), SwiftUI esegue automaticamente un nuovo recupero e ridisegna tutte le View che utilizzano questa fetch request. Lo sviluppatore non deve chiamare manualmente refresh, iscriversi a NSManagedObjectContextDidSaveNotification o ricaricare i dati. SwiftUI gestisce l'intero ciclo di sincronizzazione.

Affinché @FetchRequest funzioni, è necessario un NSManagedObjectContext disponibile nell'ambiente SwiftUI tramite @Environment(\.managedObjectContext). Il contesto viene solitamente passato dalla radice dell'applicazione, dove viene creato un NSPersistentContainer. Il pattern standard è utilizzare una ViewModel o una struct App per configurare lo stack di Core Data e passare il contesto tramite il modificatore .environment().

Ordinamento e predicati di FetchRequest

@FetchRequest accetta diversi parametri per configurare il recupero. SortDescriptors — un array di NSSortDescriptor che determina l'ordine dei record. Predicate — un NSPredicate per il filtraggio, ad esempio, recuperare solo le attività con stato “completato”. Animation — animazione per aggiornare la lista quando i dati cambiano.

I predicati di Core Data utilizzano un formato di espressioni simili a SQL: NSPredicate(format: “status == %@”, “completed”). Si possono combinare più condizioni tramite AND, OR e NOT. SortDescriptors definiscono l'ordine: NSSortDescriptor(keyPath: \Task.dueDate, ascending: true). Per ordinare per più campi, si passa un array di descrittori — il primo viene applicato come principale, gli altri come secondari.

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

Nell'esempio, @FetchRequest recupera tutte le attività incomplete (isCompleted == NO), ordinate prima per data (crescente), poi per priorità (decrescente). FetchedResults<Task> è una raccolta ad aggiornamento automatico che si comporta come un array per ForEach e List. L'animazione .default garantisce un'apparizione/scomparsa fluida dei record in caso di modifiche.

@FetchRequest con List e dati dinamici

@FetchRequest si combina perfettamente con List e ForEach per visualizzare liste dinamiche di Core Data. Il risultato del recupero (FetchedResults) è conforme al protocollo RandomAccessCollection, quindi può essere utilizzato direttamente in List e ForEach. Quando si aggiungono, eliminano o modificano record, la lista viene automaticamente ridisegnata con animazione.

Per eliminare record, si utilizza il metodo del contesto delete. Per aggiungere record, si crea un nuovo NSManagedObject e si chiama save. Tutte le modifiche vengono eseguite tramite viewContext, che insieme a @FetchRequest garantisce la consistenza dei dati. Per la modifica, si utilizza @ObservedObject o @Bindable per un oggetto Core Data specifico.

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

Questo esempio dimostra un CRUD completo con @FetchRequest. Aggiunta: creare un Task tramite il contesto e save(). Eliminazione: delete() sul contesto e save(). Aggiornamento: alternare una proprietà e save(). Dopo ogni save(), @FetchRequest recupera automaticamente i dati di nuovo, e la List viene aggiornata con animazione. I modificatori onDelete e onTapGesture forniscono interazioni iOS standard.

Esempi di codice @FetchRequest

Consideriamo un esempio completo di applicazione di diario con @FetchRequest per il recupero di record. Il modulo di ricerca utilizza un predicato dinamico: durante l'inserimento del testo, FetchRequest aggiorna automaticamente i risultati, filtrando i record per titolo e contenuto. L'ordinamento è per data di creazione, dal più recente al più vecchio.

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

Diario con ricerca: durante l'inserimento del testo nel campo searchable, viene creato un NSPredicate con CONTAINS[c] (ricerca senza distinzione tra maiuscole e minuscole) sui campi title e content. Init accetta searchText e crea un FetchRequest con il predicato corrispondente. Il modificatore searchable aggiorna automaticamente searchText, e @FetchRequest reagisce al cambio di predicato e recupera nuovamente i dati.

Per scenari più complessi con più filtri, combinare NSCompoundPredicate con logica AND/OR. @FetchRequest supporta predicati di qualsiasi complessità, incluse sottoquery annidate (SUBQUERY) per il filtraggio per relationship. Tuttavia, ricordare: i predicati complessi influenzano le prestazioni — per set di dati grandi (oltre 10.000 record), utilizzare l'indicizzazione di Core Data e limitare il recupero tramite fetchLimit.

Predicati dinamici in fase di esecuzione

@FetchRequest supporta predicati dinamici tramite il parametro predicate, che può essere modificato in fase di esecuzione. Per farlo, si crea un @FetchRequest con un predicato variabile e lo si passa tramite Binding. Quando l'utente cambia il filtro — ad esempio, seleziona una categoria di attività — il predicato viene aggiornato e il recupero si riavvia automaticamente.

I predicati dinamici vengono implementati tramite una proprietà @State o @Published, che viene passata come Binding all'inizializzatore di @FetchRequest. SwiftUI tiene traccia delle modifiche a questo Binding e ricrea il NSFetchRequest con il nuovo predicato, aggiornando automaticamente la lista.

Domande frequenti

Come passare managedObjectContext a @FetchRequest?

Aggiungere @Environment(\.managedObjectContext) var viewContext nella View radice e passarlo tramite il modificatore .environment(\.managedObjectContext, context). Tutte le View figlie che utilizzano @FetchRequest riceveranno automaticamente il contesto dall'ambiente.

Si può aggiornare @FetchRequest senza ricaricare la View?

Sì, modificare il contesto di Core Data: aggiungere, eliminare o aggiornare un oggetto e chiamare save(). @FetchRequest eseguirà automaticamente un nuovo recupero e aggiornerà l'interfaccia. Per un aggiornamento forzato senza save(), utilizzare viewContext.refreshAllObjects().

Qual è la differenza tra @FetchRequest e @SectionedFetchRequest?

@FetchRequest restituisce un array piatto di oggetti. @SectionedFetchRequest raggruppa i risultati per un key path specificato in sezioni, simile a UITableView con sectionNameKeyPath. Utilizzare @SectionedFetchRequest per liste raggruppate con intestazioni di sezione.

Come utilizzare @FetchRequest con le Relationships?

Per filtrare per relationship, utilizzare la notazione punto: NSPredicate(format: “category.name == %@”, “Work”). @FetchRequest esegue automaticamente un JOIN in SQLite. Per accedere a una relationship tramite un oggetto selezionato, utilizzare le normali proprietà di NSManagedObject.

@FetchRequest supporta la paginazione?

@FetchRequest non supporta la paginazione direttamente, poiché è progettato per visualizzare tutti i record con aggiornamento reattivo. Per la paginazione, utilizzare NSFetchRequest con fetchLimit/fetchOffset e caricare i dati manualmente durante lo scorrimento della lista.

Riepilogo

  • @FetchRequest — un property wrapper per il recupero reattivo automatico di Core Data in SwiftUI
  • Ordinamento impostato da un array di NSSortDescriptor, filtraggio da NSPredicate
  • Aggiornamento automatico dell'interfaccia a qualsiasi modifica dei dati nel contesto di Core Data
  • managedObjectContext viene passato tramite @Environment nella gerarchia delle View
  • Predicati dinamici implementati tramite un init personalizzato con parametro predicato
  • Operazioni CRUD eseguite tramite viewContext con sincronizzazione automatica della lista
  • Utilizzare @FetchRequest per visualizzare Core Data, @SectionedFetchRequest per liste raggruppate

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche