Goodbye NSPredicate, hello Realm Swift Query API

The Realm team has just released a new query API for our Swift SDK. I write about it in Goodbye NSPredicate, hello Realm Swift Query API.

I’m not a fan of writing code using pseudo-English text strings. It’s a major context switch when you’ve been writing “native” code. Compilers don’t detect errors in the strings, whether syntax errors or mismatched types, leaving you to learn of your mistakes when your app crashes.

I spent more than seven years working at MySQL and Oracle, and still wasn’t comfortable writing anything but the simplest of SQL queries. I left to join MongoDB because I knew that the object/document model was the way that developers should work with their data. I also knew that idiomatic queries for each programming language were the way to go.

That’s why I was really excited when MongoDB acquired Realm—a leading mobile object database. You work with Realm objects in your native language (in this case, Swift) to manipulate your data.

However, there was one area that felt odd in Realm’s Swift SDK. You had to use NSPredicate when searching for Realm objects that match your criteria. NSPredicates are strings with variable substitution. :man_facepalming:

NSPredicates are used when searching for data in Apple’s Core Data database, and so it was a reasonable design decision. It meant that iOS developers could reuse the skills they’d developed while working with Core Data.

But, I hate writing code as strings.

The good news is that the Realm SDK for Swift has added the option to use type-safe queries through the Realm Swift Query API. :partying_face:.

You now have the option whether to filter using NSPredicates:


let predicate = NSPredicate(format: "isSoft == %@", NSNumber(value: wantSoft)

let decisions = unfilteredDecisions.filter(predicate)

or with the new Realm Swift Query API:


let decisions = unfilteredDecisions.where { $0.isSoft == wantSoft }

In Goodbye NSPredicate, hello Realm Swift Query API, I show you some examples of how to use the Realm Swift Query API. I also show you an example where wrangling with NSPredicate strings has frustrated me.

3 Likes

Type-safe filtering is a tremendous addition to Realm and that article is really good. Thanks for putting that together.

A question:

Suppose I would like to create a filter based on the text in a popup button. For example; a popup contains a, b, and c, and a, b and c are fields on a Realm Object

class SomeObject: Object {
    @Persisted var a = ""
    @Persisted var b = ""
    @Persisted var c = ""
}

With NSPredicates, a dynamic filter can be created based on what the user selects in the popup; suppose they select a and provide some text to find

let results = realm.objects(SomeObject.self).filter("%@ == %@", popupSelection, textToFind)

If they select b or c, that same filter will filter on b field or c field.

Now want to do that same thing in a type-safe way so the ‘template’ would be this

let results = realm.objects(SomeObject.self).where { $0.a == textToFind }

or this

let results = realm.objects(SomeObject.self).where { theObject in
   theObject.a == textToFind
}

obviously that filters on just the a field. So we try this

let results = realm.objects(SomeObject.self).where { theObject in 
   switch userSelected {   //a b or c
      case "a":
          theObject.a == textToFind
     case "b":
          theObject.b == textToFind

but that results in an err here theObject.a == textToFind

Referencing operator function ‘==’ on ‘StringProtocol’ requires that ‘Query’ conform to ‘StringProtocol’

There’s probably a simple solution but curious why the error in the switch?

@Jay The issue you are getting in the switch statement is likely related to not having any return in each case. Could you try:

let results = realm.objects(SomeObject.self).where { theObject in 
   switch userSelected {   //a b or c
      case "a":
          return theObject.a == textToFind
     case "b":
          return theObject.b == textToFind
1 Like

@Lee_Maguire1 Of course! Thank you for the clarification.