NEW: Subscribe to Hacking with Swift+ and accelerate your learning! >>

Filtering @FetchRequest using NSPredicate

Paul Hudson    @twostraws   

When we use SwiftUI’s @FetchRequest property wrapper, we can provide an array of sort descriptors to control the ordering of results, but we can also provide an NSPredicate to control which results should be shown. Predicates are simple tests, and the test will be applied to each object in our Core Data entity – only objects that pass the test will be included in the resulting array.

The syntax for NSPredicate isn’t something you can guess easily, but realistically you’re only ever going to want a few types of predicate so it’s not as bad as you think.

To try out some predicates, create a new entity called Ship with two string attributes: “name” and “universe”.

Now modify ContentView.swift to this:

import CoreData
import SwiftUI

struct ContentView: View {
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(entity: Ship.entity(), sortDescriptors: [], predicate: nil) var ships: FetchedResults<Ship>

    var body: some View {
        VStack {
            List(ships, id: \.self) { ship in
                Text( ?? "Unknown name")

            Button("Add Examples") {
                let ship1 = Ship(context: self.moc)
       = "Enterprise"
                ship1.universe = "Star Trek"

                let ship2 = Ship(context: self.moc)
       = "Defiant"
                ship2.universe = "Star Trek"

                let ship3 = Ship(context: self.moc)
       = "Millennium Falcon"
                ship3.universe = "Star Wars"

                let ship4 = Ship(context: self.moc)
       = "Executor"
                ship4.universe = "Star Wars"


We can now press the button to inject some sample data into Core Data, but right now we don’t have a predicate. To fix that we need to replace the nil predicate value with some sort of test that can be applied to our objects.

For example, we could ask for ships that are from Star Wars, like this:

@FetchRequest(entity: Ship.entity(), sortDescriptors: [], predicate: NSPredicate(format: "universe == 'Star Wars'")) var ships: FetchedResults<Ship>

That gets complicated if your data includes quote marks, so it’s more common to use special syntax instead: `%@‘ means “insert some data here”, and allows us to provide that data as a parameter to the predicate rather than inline.

So, we could instead write this:

NSPredicate(format: "universe == %@", "Star Wars"))

As well as ==, we can also use comparisons such as < and > to filter our objects. For example this will return Defiant, Enterprise, and Executor:

NSPredicate(format: "name < %@", "F")) var ships: FetchedResults<Ship>

%@ is doing a lot of work behind the scenes, particularly when it comes to converting native Swift types to their Core Data equivalents. For example, we could use an IN predicate to check whether the universe is one of three options from an array, like this:

NSPredicate(format: "universe IN %@", ["Aliens", "Firefly", "Star Trek"])

We can also use predicates to examine part of a string, using operators such as BEGINSWITH and CONTAINS. For example, this will return all ships that start with a capital E:

NSPredicate(format: "name BEGINSWITH %@", "E"))

That predicate is case-sensitive; if you want to ignore case you need to modify it to this:

NSPredicate(format: "name BEGINSWITH[c] %@", "e"))

CONTAINS[c] works similarly, except rather than starting with your substring it can be anywhere inside the attribute.

Finally, you can flip predicates around using NOT, to get the inverse of their regular behavior. For example, this finds all ships that don’t start with an E:

NSPredicate(format: "NOT name BEGINSWITH[c] %@", "e"))

If you need more complicated predicates, join them using AND to build up as much precision as you need, or add an import for Core Data and take a look at NSCompoundPredicate – it lets you build one predicate out of several smaller ones.

Hacking with Swift is sponsored by Instabug

SPONSORED Are you tired of wasting time debugging your Swift app? Instabug’s SDK is here to help you minimize debugging time by providing you with complete device details, network logs, and reproduction steps with every bug report. All data is attached automatically, and it only takes a line of code to setup. Start your free trial now and get 3 months off exclusively for the Hacking with Swift Community.

Start your free trial!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Buy Pro Swift Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift (Vapor Edition) Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.4/5

Link copied to your pasteboard.