SkillAgentSearch skills...

PredicateKit

🎯 PredicateKit allows Swift developers to write expressive and type-safe predicates for CoreData using key-paths, comparisons and logical operators, literal values, and functions.

Install / Use

/learn @ftchirou/PredicateKit
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

🎯 PredicateKit

GitHub Workflow Status (branch) <img src="https://img.shields.io/badge/coverage-100%25-green"> GitHub release (latest SemVer) <img src="https://img.shields.io/badge/platforms-iOS%2011%2B%20%7C%20macOS%2010.15%2B%20%7C%20watchOS%205%2B%20%7C%20tvOS%2011%2B-lightgrey"> <img src="https://img.shields.io/badge/swift-%3E%3D%205.1-orange">

PredicateKit is an alternative to NSPredicate allowing you to write expressive and type-safe predicates for CoreData using key-paths, comparisons and logical operators, literal values, and functions.

<img src="https://dl.dropboxusercontent.com/s/h8yeg34jb68vxfy/predicate-kit.png">

Contents

Motivation

CoreData is a formidable piece of technology, however not all of its API has caught up with the modern Swift world. Specifically, fetching and filtering objects from CoreData relies heavily on NSPredicate and NSExpression. Unfortunately, a whole range of bugs and runtime errors can easily be introduced using those APIs. For instance, we can compare a property of type String to a value of type Int or even use a non-existant property in a predicate; these mistakes will go un-noticed at compile time but can cause important errors at runtime that may not be obvious to diagnose. This is where PredicateKit comes in by making it virtually impossible to introduce these types of errors.

Concretely, PredicateKit provides

  • a type-safe and expressive API for writing predicates. When using PredicateKit, all properties involved in your predicates are expressed using key-paths. This ensures that the usage of inexistant properties or typos are caught at compile time. Additionally, all operations such as comparisons, functions calls, etc. are strongly-typed, making it impossible to write invalid predicates.
  • an improved developer experience. Enjoy auto-completion and syntax highlighting when writing your predicates. In addition, PredicateKit is just a lightweight replacement for NSPredicate, no major change to your codebase is required, no special protocol to conform to, no configuration, etc. Simply import PredicateKit, write your predicates and use the functions NSManagedObjectContext.fetch(where:) or NSManagedObjectContext.count(where:) to execute them.

Installation

Carthage

Add the following line to your Cartfile.

github "ftchirou/PredicateKit" ~> 1.0.0

CocoaPods

Add the following line to your Podfile.

pod 'PredicateKit', ~> '1.0.0'

Swift Package Manager

Update the dependencies array in your Package.swift.

dependencies: [
  .package(url: "https://github.com/ftchirou/PredicateKit", .upToNextMajor(from: "1.0.0"))
]

Quick start

Fetching objects

To fetch objects using PredicateKit, use the function fetch(where:) on an instance of NSManagedObjectContext passing as argument a predicate. fetch(where:) returns an object of type FetchRequest on which you call result() to execute the request and retrieve the matching objects.

Example
let notes: [Note] = try managedObjectContext
  .fetch(where: \Note.text == "Hello, World!" && \Note.creationDate < Date())
  .result()

You write your predicates using the key-paths of the entity to filter and a combination of comparison and logical operators, literal values, and functions calls.

See Writing predicates for more about writing predicates.

Fetching objects as dictionaries

By default, fetch(where:) returns an array of subclasses of NSManagedObject. You can specify that the objects be returned as an array of dictionaries ([[String: Any]]) simply by changing the type of the variable storing the result of the fetch.

Example
let notes: [[String: Any]] = try managedObjectContext
  .fetch(where: \Note.text == "Hello, World!" && \Note.creationDate < Date())
  .result()

Configuring the fetch

fetch(where:) returns an object of type FetchRequest. You can apply a series of modifiers on this object to further configure how the objects should be matched and returned. For example, sorted(by: \Note.creationDate, .descending) is a modifier specifying that the objects should be sorted by the creation date in the descending order. A modifier returns a mutated FetchRequest; a series of modifiers can be chained together to create the final FetchRequest.

Example
let notes: [Note] = try managedObjectContext
  .fetch(where: (\Note.text).contains("Hello, World!") && \Note.creationDate < Date())
  .limit(50) // Return 50 objects matching the predicate.
  .offset(100) // Skip the first 100 objects matching the predicate.
  .sorted(by: \Note.creationDate) // Sort the matching objects by their creation date.
  .result()

See Request modifiers for more about modifiers.

Fetching objects with the @FetchRequest property wrapper

PredicateKit extends the SwiftUI @FetchRequest property wrapper to support type-safe predicates. To use, simply initialize a @FetchRequest with a predicate.

Example
import PredicateKit
import SwiftUI

struct ContentView: View {

  @SwiftUI.FetchRequest(predicate: \Note.text == "Hello, World!")
  var notes: FetchedResults<Note>

  var body: some View {
    List(notes, id: \.self) {
      Text($0.text)
    }
  }
}

You can also initialize a @FetchRequest with a full-fledged request with modifiers and sort descriptors.

Example
import PredicateKit
import SwiftUI

struct ContentView: View {

  @SwiftUI.FetchRequest(
    fetchRequest: FetchRequest(predicate: (\Note.text).contains("Hello, World!"))
      .limit(50)
      .offset(100)
      .sorted(by: \Note.creationDate)
  )
  var notes: FetchedResults<Note>

  var body: some View {
    List(notes, id: \.self) {
      Text($0.text)
    }
  }
}

Both initializers accept an optional parameter animation that will be used to animate changes in the fetched results.

Example
import PredicateKit
import SwiftUI

struct ContentView: View {

  @SwiftUI.FetchRequest(
    predicate: (\Note.text).contains("Hello, World!"),
    animation: .easeInOut
  )
  var notes: FetchedResults<Note>

  var body: some View {
    List(notes, id: \.self) {
      Text($0.text)
    }
  }
}

You can update the predicate associated with your FetchedResults using updatePredicate.

Example
import PredicateKit
import SwiftUI

struct ContentView: View {

  @SwiftUI.FetchRequest(predicate: \Note.text == "Hello, World!")
  var notes: FetchedResults<Note>

  var body: some View {
    List(notes, id: \.self) {
      Text($0.text)
    }
    Button("Show recents") {
      let recentDate: Date = // ...
      notes.updatePredicate(\Note.createdAt >= recentDate)
    }
  }
}

This will cause the associated FetchRequest to execute a fetch with the new predicate when the Show recents button is tapped.

Fetching objects with the @SectionedFetchRequest property wrapper

Predica

Related Skills

View on GitHub
GitHub Stars412
CategoryDevelopment
Updated14d ago
Forks18

Languages

Swift

Security Score

100/100

Audited on Mar 17, 2026

No findings