Um “predicate” é uma condição booleana usada para selecionar ou filtrar registros. Ele responde “este objeto atende ao critério?” e retorna verdadeiro ou falso.
Em bancos/ORMs, o predicate é enviado para o mecanismo de persistência, que retorna apenas os registros que satisfazem a condição.
Já no ecossistema da Apple temos:
NSPredicate: API clássica (Core Data, Foundation) baseada em strings formatadas, como `NSPredicate(format: “age >= %d”, 18)` . É flexível, mas menos segura (erros só aparecem em runtime).#Predicate: DSL moderno e tipado (SwiftData/SwiftUI). Você escreve condições com código Swift e o macro gera uma expressão booleana segura em tempo de compilação.
Uso em SwiftData/SwiftUI
@Query(filter: #Predicate<Model> { … }) , o $0 representa um registro do tipo Model . Você compara campos com valores capturados:
// Exemplo com strings: #Predicate<CompanyAndLocation> { $0.company.name == companyName && $0.location.location == locationName }// Exemplo por identidade:#Predicate<CompanyAndLocation> { $0.company.id == companyId && $0.location.id == locationId } |
jj
Isso permite que o filtro rode no store, retornando apenas os itens que batem com o critério.
Quando usar o #Predicate?
- Filtrar uma coleção persistida via @Query ou FetchDescriptor para trazer do banco somente o que importa.
- Substitui NSPredicate quando você quer segurança de tipos e integração direta com SwiftData.
Quais são as vantagens do #Predicate?
- Tipado e checado em compile-time.
- Integra-se ao @Query e FetchDescriptor , reduzindo erros de formatação.
- Melhora completions e refatorações (se você renomear um campo, o compilador ajuda).
Cuidado com algumas pequenas armadilhas!?
- Comparar “key path com key path” em vez de “campo com valor” causa erros de tipo no DSL.
- Referenciar self ou propriedades de @Model dentro do bloco sem capturar valores simples pode quebrar a expansão do macro.
- Comparar relacionamentos por objeto inteiro pode falhar dependendo da versão do SDK; comparar por id ou por atributos únicos é mais robusto.
Show me the code:
// PredicateApp.swift
|
// CompanyAndLocation.swift
|
// DataView.swift
|
Crie um projeto SwiftUI, adicione estes 3 arquivos e compile o projeto. E se você este erro significa que está tudo certo (em receber o erro).
Cannot convert value of type 'PredicateExpressions.Conjunction<PredicateExpressions. |
Esse erro indica que estamos tentando comparar objetos complexos diretamente, quando o #Predicate só aceita comparações de tipos escalares (Int, String, UUID, etc.). Também o erro indica que o fechamento de Predicate está retornando um tipo interno do DSL (Conjunction<Equal<…>> ) que não está sendo aceito como any StandardPredicateExpression, normalmente por construção incorreta do predicate (mistura de variável x valor ou uso de key paths em vez de valores).
O problema está nesta parte do código:
let predicate = #Predicate<CompanyAndLocation>{$0.company == self.company && $0.location == self.location} |
Poderíamos resolver assim:
let companyName = company.namelet locationName = location.locationlet predicate = #Predicate<CompanyAndLocation> {$0.company.name == companyName && $0.location.location == locationName} |
Se eu não precisasse comparar o objeto completo. Mas e se eu precisasse?Neste caso para 2 propriedades seria simples, mas se este objeto tivesse mais propriedades? E se esse objeto fosse atualizado com mais propriedades com o decorrer do tempo? Então eu teria que voltar nesse código para atualizá-lo e garantir uma comparação completa.
Podemos tentar assim:
let predicate = #Predicate<CompanyAndLocation> {$0.company.id == company.id && $0.location.id == location.id} |
Aqui estamos tentando acessar company.id e location.id diretamente. O macro não consegue rastrear essas propriedades dinâmicas de objetos externos. Ele espera valores simples e escalares que possam ser “capturados” como constantes. Ou seja, não funciona.
A solução: Compare os identificadores, não os objetos:
Essa é uma questão comum com Swift’s #Predicate macro. O problema está em como o macro captura variáveis. O #Predicate funciona através de type-safe query generation, ou seja, precisa traduzir sua expressão Swift para uma query que pode ser executada no banco de dados (CoreData ou SwiftData).
Então resolvemos assim:
let companyId = company.idlet locationId = location.idlet predicate = #Predicate<CompanyAndLocation> {$0.company.id == companyId && $0.location.id == locationId} |
- As variáveis companyId e locationId são capturadas pelo closure como valores simples (Equatable)
-
O macro consegue identificá-las como constantes externas e substituí-las corretamente na query
Conclusão:
O #Predicate só consegue traduzir para queries de banco de dados expressões que comparam valores escalares (números, strings, UUIDs). Comparações entre objetos complexos não podem ser traduzidas para SQL/query de dados.
Então dentro de um #Predicate, compare os identificadores e não os objetos, sempre extraia os valores que você quer comparar em variáveis locais simples antes de usá-las no closure. Isso permite que o macro entenda exatamente quais constantes devem ser “compiladas” na query.




