PredicateFlow
  • March 28, 2024

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.

Supported platforms


  • iOS 9.0+
  • macOS 10.9+
  • tvOS 9.0+
  • watchOS 2.0+

Installation


CocoaPods is actually the only way to install it.

Cocoapods

CocoaPods 0.39.0+ is required to build this library

  1. Add pod 'PredicateFlow' to your Podfile and run pod install

  2. In Xcode, click on your project in the file list, choose your target under TARGETS, click the Build Phases tab and add a New Run Script Phase by clicking the plus icon in the top left

  3. Drag the new Run Script phase above the Compile Sources phase and below Check 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

  1. Build your project. In Finder you will now see a PredicateFlow.generated.swift in the $SRCROOT-folder, drag the PredicateFlow.generated.swift files into your project and uncheck Copy items if needed

Tip: Add the *.generated.swift pattern to your .gitignore file to prevent unnecessary conflicts.

Usage


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 owner.dogs.@max.age > %@ 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”

PredicateFlow/Realm


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)

GitHub


View Github

#cocoapods #coredata #flow #ios #library #macos #pods #realmswift #swift #tvos #watchos
YOU MIGHT ALSO LIKE...
FigmaPreviewSwiftUI

A Figma component preview for your SwiftUI views. You can use Figma components instead of real views within your app ...

SafePreviewDevice

Motivation At WWDC 2019, Apple announced SwiftUI a new library for building UI in a simple and fast way. Xcode’s ...

PreviewView

Make use of SwiftUI previews for rapidly prototyping your UIViewControllers and UIViews! The SwiftUI preview canvas is tied to a specific version of ...

PreviewDevice

Requirements   Dev environment: Xcode 13+, macOS 12+ iOS 13.0+, macOS 10.15+, Mac Catalyst 13.0+, tvOS 13.0+, watchOS 6.0+ Usage

SwiftUIWheelPicker

Horizontal wheel picker for SwiftUI Requirements iOS 13.0+ Installation CocoaPods