@FetchRequest no SwiftUI — um property wrapper para buscar dados automaticamente do Core Data com atualização reativa da interface. O wrapper aceita NSFetchRequest com predicados de filtro e classificações, e o resultado — um array de NSManagedObject — é atualizado automaticamente em qualquer alteração no contexto do Core Data. Segundo a Apple Developer Documentation (2025), @FetchRequest requer managedObjectContext no ambiente do SwiftUI e suporta predicados dinâmicos, fornecendo uma conexão reativa entre Core Data e a interface do usuário.
Pontos principais
@FetchRequest é um property wrapper do SwiftUI que cria e gerencia um FetchRequest do Core Data. Na inicialização, o wrapper aceita um tipo de entidade, um array de NSSortDescriptor para classificação e um NSPredicate opcional para filtragem. O resultado é armazenado como um array de objetos do tipo NSManagedObject especificado e é atualizado automaticamente quando ocorrem alterações na camada de persistência.
A principal característica do @FetchRequest é a reatividade. Quando os dados no Core Data mudam (save, insert, delete, update), o SwiftUI automaticamente realiza uma nova busca e redesenha todas as Views que usam este fetch request. O desenvolvedor não precisa chamar refresh manualmente, se inscrever no NSManagedObjectContextDidSaveNotification ou recarregar os dados. SwiftUI gerencia todo o ciclo de sincronização.
Para o @FetchRequest funcionar, é necessário um NSManagedObjectContext disponível no ambiente do SwiftUI através de @Environment(\.managedObjectContext). O contexto é normalmente passado da raiz da aplicação, onde um NSPersistentContainer é criado. O padrão comum é usar uma ViewModel ou struct App para configurar a stack do Core Data e passar o contexto através do modificador .environment().
@FetchRequest aceita vários parâmetros para configurar a busca. SortDescriptors — um array de NSSortDescriptor que determina a ordem dos registros. Predicate — um NSPredicate para filtragem, por exemplo, buscar apenas tarefas com status “concluído”. Animation — animação para atualizar a lista quando os dados mudam.
Os predicados do Core Data usam um formato de expressões semelhantes a SQL: NSPredicate(format: “status == %@”, “completed”). É possível combinar múltiplas condições usando AND, OR e NOT. SortDescriptors definem a ordem: NSSortDescriptor(keyPath: \Task.dueDate, ascending: true). Para classificar por vários campos, um array de descritores é passado — o primeiro é aplicado como principal, os restantes como secundários.
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 ?? "")
}
}
}
No exemplo, @FetchRequest busca todas as tarefas incompletas (isCompleted == NO), classificadas primeiro por data (ascendente), depois por prioridade (descendente). FetchedResults<Task> é uma coleção que se atualiza automaticamente e se comporta como um array para ForEach e List. A animação .default garante aparecimento/desaparecimento suave dos registros nas alterações.
@FetchRequest combina perfeitamente com List e ForEach para exibir listas dinâmicas do Core Data. O resultado da busca (FetchedResults) está em conformidade com o protocolo RandomAccessCollection, portanto pode ser usado diretamente em List e ForEach. Ao adicionar, excluir ou modificar registros, a lista é redesenhada automaticamente com animação.
Para excluir registros, usa-se o método do contexto delete. Para adicionar registros, um novo NSManagedObject é criado e save é chamado. Todas as alterações são realizadas através do viewContext, que junto com @FetchRequest garante a consistência dos dados. Para edição, usa-se @ObservedObject ou @Bindable para um objeto específico do 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: "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()
}
}
Este exemplo demonstra um CRUD completo com @FetchRequest. Adicionar: criar uma Task através do contexto e save(). Excluir: delete() no contexto e save(). Atualizar: alternar uma propriedade e save(). Após cada save(), @FetchRequest busca novamente os dados automaticamente, e a List é atualizada com animação. Os modificadores onDelete e onTapGesture fornecem interações padrão do iOS.
Vamos ver um exemplo completo de um aplicativo de diário com @FetchRequest para busca de registros. O formulário de pesquisa usa um predicado dinâmico: ao digitar texto, o FetchRequest atualiza automaticamente os resultados, filtrando registros por título e conteúdo. A classificação é por data de criação, do mais recente ao mais antigo.
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)
}
}
Diário com pesquisa: ao digitar texto no campo searchable, é criado um NSPredicate com CONTAINS[c] (pesquisa sem distinção de maiúsculas/minúsculas) nos campos title e content. Init aceita searchText e cria um FetchRequest com o predicado correspondente. O modificador searchable atualiza automaticamente searchText, e @FetchRequest reage à mudança do predicado e busca novamente os dados.
Para cenários mais complexos com vários filtros, combine NSCompoundPredicate com lógica AND/OR. @FetchRequest suporta predicados de qualquer complexidade, incluindo subconsultas aninhadas (SUBQUERY) para filtrar por relationship. No entanto, lembre-se: predicados complexos afetam o desempenho — para grandes conjuntos de dados (mais de 10.000 registros), use indexação do Core Data e limite a busca através de fetchLimit.
@FetchRequest suporta predicados dinâmicos através do parâmetro predicate, que pode ser alterado em tempo de execução. Para isso, cria-se um @FetchRequest com um predicado variável e passa-se via Binding. Quando o usuário altera o filtro — por exemplo, seleciona uma categoria de tarefas — o predicado é atualizado e a busca é reiniciada automaticamente.
Predicados dinâmicos são implementados através de uma propriedade @State ou @Published, que é passada como Binding para o inicializador do @FetchRequest. O SwiftUI rastreia alterações neste Binding e recria o NSFetchRequest com o novo predicado, atualizando automaticamente a lista.
Perguntas frequentes
Adicione @Environment(\.managedObjectContext) var viewContext na View raiz e passe através do modificador .environment(\.managedObjectContext, context). Todas as Views filhas que usam @FetchRequest receberão automaticamente o contexto do ambiente.
Sim, modifique o contexto do Core Data: adicione, exclua ou atualize um objeto e chame save(). @FetchRequest realizará automaticamente uma nova busca e atualizará a UI. Para uma atualização forçada sem save(), use viewContext.refreshAllObjects().
@FetchRequest retorna um array plano de objetos. @SectionedFetchRequest agrupa resultados por um key path especificado em seções, similar ao UITableView com sectionNameKeyPath. Use @SectionedFetchRequest para listas agrupadas com cabeçalhos de seção.
Para filtrar por relationship, use notação de ponto: NSPredicate(format: “category.name == %@”, “Work”). @FetchRequest realiza automaticamente um JOIN no SQLite. Para acessar uma relationship através de um objeto selecionado, use as propriedades normais de NSManagedObject.
@FetchRequest não suporta paginação diretamente, pois é projetado para exibir todos os registros com atualização reativa. Para paginação, use NSFetchRequest com fetchLimit/fetchOffset e carregue os dados manualmente ao rolar a lista.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também