Multiple Word, Random Order Search – CoreData & SwiftUI

Today I worked on the search functionality for the upcoming Product Management app. Given that this app will contain lots of long notes, having good search is essential.

Screen Shot 2021 08 27 at 5 06 07 PM

In most cases & for most apps, a simple one to one comparison will do the job great -like in the above image-. Here’s how a predicate implementation one to one could look like. Lets say you have a list of names in Core Data and you want to search for John in them:

let pred = NSPredicate(format: “name CONTAINS[cd] %@”, searchTerm)

Great! Well… great for the use case mentioned, but what if instead of names you have long notes and the search words could be out of order? Not. So. Great…

Lets imagine the following two notes:

1. “Today John works at the factory, he delivered 10 new bottles”

2. “We’re not sure what we’ll do with John this weekend, maybe we’ll work on the paper due next week”

The NSPredicate we typed above would match the former but totally miss the latter. That’s because in the first line text contains the combination “John works” one to one (in that precise order) while in the second line the text does not. 

In the second example words are out of order so our predicate misses them. To solve this, let me introduce you to NSCompoundPredicate

NSCompoundPredicate allows you to combine multiple predicates in one array that is searched in one CoreData fetch, making it hugely powerful. 

Here’s a swiftUI implementation that would handle the out of order use-case:

    .onChange(of: searchableText) { searchableText in

        var predicateList: Array<NSPredicate> = []

        if !searchableText.isEmpty {

            for iterator in searchableText.components(separatedBy: ” “){

                if iterator == “”{

                    continue

                }

                let pred = NSPredicate(format: “text CONTAINS[cd] %@”, iterator)

                predicateList.append(pred)

            }

            someElement.nsPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicateList)

        }

    }

In this example I take the search query, split it in words and create a predicate for each. As long as the note contains the words, does not matter the order, it’ll find the right match. 

NOTE: If you don’t know what’s [c] and [d], that’s a way to tell NSPredicate it should not care about capitalization nor diacritics for the given keyword, in our case ‘CONTAINS’. 

That’s it, nice and easy. 

Happy Friday, 

Marc