PredicateBuilder
PredicateBuilder 是一种声明式、类型安全的创建 NSPredicate
类型的方式,用于约束 Core Data 检索和集合过滤。
🧱 使用 DSL、键路径、简单的包含运算符类型如 And
,以及 SwiftUI 类似的修饰符构建谓词
🔍 在编译时可视化和理解谓词问题,并告别运行时崩溃
📚 将密集、复杂的谓词格式字符串转换为可组合和可读的谓词
🧩 无缝集成到现有代码库中,因为所有 @PredicateBuilder
类型都构建一个正常的 NSPredicate
Foundation.#Predicate
一样吗?
这不是和 更重要的是!Swift 5.9 中引入的 Predicate
类型在处理谓词时提高了类型安全性,尤其是与 SwiftData 结合使用时。不幸的是,它也带来了与 NSPredicate 格式字符串语法的相同一些缺点,例如,当查询一个不支持 Core Data 的类型时,会导致运行时崩溃。
PredicateBuilder 提供了增强的编译时安全性以及一个用于组合谓词的 DSL。
使用方法
// Simple predicates
let namePredicate: NSPredicate = \Candy.name == "Reese's"
@PredicateBuilder<Candy> var isSweetPredicate: NSPredicate {
\Candy.isSweet
}
// Compound predicates
let isSharingSize: NSPredicate = \Candy.isKingSize || \Candy.servings > 2
@PredicateBuilder<Candy> var isTake5Bar: NSPredicate { // 🍫 Not sponsored 🥨
And {
\Candy.ingredients.contains(.chocolate)
\Candy.ingredients.contains(.peanuts)
\Candy.ingredients.contains(.caramel)
\Candy.ingredients.contains(.peanutButter)
// Dropping the leading type name is allowed when the compiler can infer it
\.ingredients.contains(.pretzels)
}
}
// Modifiers
let isChocolate: NSPredicate = Value(\Candy.ingredients).contains(.cocoa)
@PredicateBuilder<Candy> var caseInsensitiveNameLikePredicate: NSPredicate {
Value(\.name)
.like("snickers")
.withOptions(.caseInsensitive)
}
// More complex predicate composed of subpredicates:
// Use the TypedPredicate type to compose predicates from other predicates.
// This preserves the type information needed to build the final result.
@PredicateBuilder var isSweetPredicate: some TypedPredicate<Candy> {
\.isSweet
}
@PredicateBuilder<Candy> var complexCandyPredicate: NSPredicate {
if isSweetToothActive {
isSweetPredicate
}
for name in favoriteCandyNames {
\.name == name
}
Or {
// Any of our favorite candy shop's candies that are chocolate
// Resolves to 'ANY candiesInStock.isChocolate == 1' in the NSPredicate expression language
AnyMembers(of: \CandyShop.favorite.candiesInStock) {
\.isChocolate
}
// We dislike Almonds so much that we only want the halloween basket contents if
// there isn't a single Almond Joy in there
NoMembers(of: \HalloweenBasket.current.contents) {
\.name == "Almond Joy"
}
}
}
// ❌ Invalid predicates that won't compile
\String.count == 5 // Operator function '==' requires that 'String' inherit from 'NSManagedObject'
\Candy.isSweet == 24 // Cannot compare right hand operand type that does not match key path's `Value` type
@PredicateBuilder<Candy> var predicate: NSPredicate {
\NSString.boolValue == true // NSString.self != Candy.self
}
要求
使用@PredicateBuilder
查询的类型必须是NSManagedObject
子类。这确保了你查询的每个属性都支持NSPredicate
表达式语言,并保护你的代码免受潜在崩溃的影响。
安装
Swift 包管理器
将以下内容作为依赖项添加到你的Package.swift
中
dependencies: [
.package(url: "https://github.com/square/predicate-builder", from: "1.0.0")
]
或者导航到你的 Xcode 项目,然后选择添加包依赖项
,搜索PredicateBuilder
。