WWDC24 SALE: Save 50% on all my Swift books and bundles! >>

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(sortDescriptors: [], predicate: nil) var ships: FetchedResults<Ship>

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

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

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

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

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

                try? moc.save()

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(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"))

%@ 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.

Save 50% in my WWDC sale.

SAVE 50% To celebrate WWDC24, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Buy Pro Swift Buy Pro SwiftUI 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 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 Beyond Code

Was this page useful? Let us know!

Average rating: 4.6/5

Unknown user

You are not logged in

Log in or create account

Link copied to your pasteboard.