Write amazing, strong-typed and easy-to-read NSPredicate. This library allows you to write flowable NSPredicate, without guessing attribution names, predicate operation or writing wrong arguments type.
- iOS 9.0+
- macOS 10.9+
- tvOS 9.0+
- watchOS 2.0+
CocoaPods is actually the only way to install it.
CocoaPods 0.39.0+ is required to build this library
-
Add
pod 'PredicateFlow'
to your Podfile and runpod install
-
In Xcode, click on your project in the file list, choose your target under
TARGETS
, click theBuild Phases
tab and add aNew Run Script Phase
by clicking the plus icon in the top left -
Drag the new
Run Script
phase above theCompile Sources
phase and belowCheck Pods Manifest.lock
, expand it and paste the following script:"$PODS_ROOT/Sourcery/bin/sourcery" --sources "$PODS_ROOT/PredicateFlow/PredicateFlow/Classes/Utils/" --sources "$SRCROOT" --templates "$PODS_ROOT/PredicateFlow/PredicateFlow/Templates/PredicateFlow.stencil" --output "$SRCROOT/PredicateFlow.generated.swift"
In case you are using PredicateFlow-Realm, past the following script instead of the above one:
"$PODS_ROOT/Sourcery/bin/sourcery" --sources "$PODS_ROOT/PredicateFlow/PredicateFlow/Classes/Utils/" --sources "$SRCROOT" --sources "$PODS_ROOT/RealmSwift" --templates "$PODS_ROOT/PredicateFlow/PredicateFlow/Templates/PredicateFlow-Realm.stencil" --output "$SRCROOT/PredicateFlow.generated.swift"
For Xcode 10 only, add a new
Output Files
and paste the following:$SRCROOT/PredicateFlow.generated.swift
-
Build your project. In Finder you will now see a
PredicateFlow.generated.swift
in the$SRCROOT
-folder, drag thePredicateFlow.generated.swift
files into your project and uncheckCopy items if needed
Tip: Add the *.generated.swift
pattern to your .gitignore
file to prevent unnecessary conflicts.
Define a class/struct with its own attributes, which implements PredicateSchema
:
import PredicateFlow
class Dog: PredicateSchema {
private var name: String = ""
private var age: Int = 0
private var isHungry: Bool = false
private var owner: Person?
}
class Person: PredicateSchema {
enum Sex {
case male
case female
}
private var name: String = ""
private var birth: Date?
private var sex: Sex!
private var dogs: [Dog] = []
}
Build the project. PredicateFlow will autogenerate attributes references to build predicates, putting them in structures.
// Generated using Sourcery 0.10.0 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
import PredicateFlow
/// The "Dog" Predicate Schema
internal struct DogSchema: GeneratedPredicateSchema {
private var compoundFieldBuilder: CompoundFieldBuilder
/// DO NOT USE THIS INIT DIRECTLY!
internal init(compoundFieldBuilder: CompoundFieldBuilder) {
self.compoundFieldBuilder = compoundFieldBuilder
}
/// "name" property
internal var name: StringPredicateProperty { return builder.string("name") }
/// "name" property for static access
internal static var name: StringPredicateProperty { return DogSchema().name }
// Other properties of Dog class autogenerated...
}
/// The "Person" Predicate Schema
internal struct PersonSchema: GeneratedPredicateSchema {
private var compoundFieldBuilder: CompoundFieldBuilder
/// DO NOT USE THIS INIT DIRECTLY!
internal init(compoundFieldBuilder: CompoundFieldBuilder) {
self.compoundFieldBuilder = compoundFieldBuilder
}
/// "name" property
internal var name: StringPredicateProperty { return builder.string("name") }
/// "name" property for static access
internal static var name: StringPredicateProperty { return PersonSchema().name }
// Other properties of Person class autogenerated...
}
To type a floawable NSPredicate, just write:
DogSchema.age.isEqual(10).query()
// or
// Vanilla mode:
// NSPredicate("age == %@", 10)
You can also write compound predicate, and use deeper fields:
PredicateBuilder(DogSchema.age > 10)
.and(DogSchema.isHungry.isTrue)
.and(DogSchema.age.between(1, 10))
.and(DogSchema.owner.element().name == "Foo")
.or(DogSchema.owner.element().dogs.maxElements().age > 10)
.or(DogSchema.owner.element().dogs.anyElements().name == "Foo")
.build()
// Vanilla mode:
// NSPredicate("age > %@ AND isHungry == %@ AND age BETWEEN %@ AND owner.name == %@ OR [email protected] > %@ OR ANY owner.dogs.name == %@", 10, true, [1, 10], "Foo", 10, "Foo")
PredicateFlow can also build KeyPaths, and you can use it to get a strong-typed one.
DogSchema.age.keyPath()
DogSchema.owner.element().dogs.keyPath()
// Vanilla mode:
// "age"
// "owner.dogs"
If you want to use flowable and strong-typed queries in Realm, add both pod 'PredicateFlow'
and pod 'PredicateFlow/Realm'
to your Podfile and run pod install
.
let realm = try! Realm()
realm.objects(Dog.self)
.filter(DogSchema.age.isGreater(than: 10))
.filter(DogSchema.isHungry.isTrue)
.sorted(DogSchema.age.ascending())
// Vanilla mode:
realm.objects(Dog.self)
.filter("age > %@", 10)
.filter("isHungry == %@", true)
.sorted("age", ascending: true)
PredicateFlow is an open source project, so feel free to contribute. You can open an issue for problems or suggestions, and you can propose your own fixes by opening a pull request with the changes.
PredicateFlow is available under the MIT license. See the LICENSE file for more info.
This library is powered by Sourcery.
Andrea Del Fante, [email protected]