PredicateBuilder 1.0.2

PredicateBuilder 1.0.2

Patrick Gatewood 维护。



  • 作者:
  • Patrick Gatewood

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