Home > Enterprise >  Explain this code: uses a string as filter function
Explain this code: uses a string as filter function

Time:10-16

When reviewing a bit of Swift code in one project I supervise, I came across this:

return (realm?.objects(ExerciseDBObject.self).filter("isDeleted == false")) as! Results<ExerciseDBObject>

What stuck out to me as a JVM/Python/JS programmer was the filter("isDeleted == false") bit. Supposedly, this code works fine: it filters the exercises that are not deleted, but it is a string. How does this work?

I have not worked with Swift, and when googling I just came across the docs on String#filter that seemed to imply that I would normally have written that bit of code as filter({!$0.isDeleted}).

The isDeleted bit of the string refers to a prop on the object. How does Swift avoid binding that to some variable that is also called isDeleted (if such was present, which it was not in this code block)?

CodePudding user response:

It's using:

// MARK: Filtering
/**
 Returns a `Results` containing all objects matching the given predicate in the collection.
 - parameter predicateFormat: A predicate format string, optionally followed by a variable number of arguments.
 */
func filter(_ predicateFormat: String, _ args: Any...) -> Results<Element>

Source: RealmCollectiton

Under the hood, it's using NSPredicate(format:), it "hides" it by simplificating and avoid writing each time NSPredicate(format: ...), and should use KeyPath too. More explaination on what you can do with that here at Predicate Format String Syntax.

not

func filter(_ isIncluded: (Self.Element) throws -> Bool) rethrows -> [Self.Element]

on Sequence

Here a sample code to illustrate/mimic:

class TestClass: NSObject {
    @objc var title: String
    init(title: String) {
        self.title = title
        super.init()
    }
    override var description: String {
        "TestClass - title: \(title)"
    }
}

let objects: [TestClass] = [TestClass(title: "Title1"), TestClass(title: "Title2")]

// In a "Swifty way
let searchedText = "Title1"
let filtered1 = objects.filter {
    $0.title == searchedText
}
print("filtered1: \(filtered1)")

// With a NSPredicate, which is more Objective-C (filtering on NSArray for instance)
// But also there is a translation with Core-Data, and then fetching optimization
let filtered2 = objects.filter {
    NSPredicate(format: "title == 'Title1'").evaluate(with: $0)
}
print("filtered2: \(filtered2)")
// Same, but with avoiding hard coding strings
let filtered3 = objects.filter {
    NSPredicate(format: "%K == %@", #keyPath(TestClass.title), searchedText).evaluate(with: $0)
}
print("filtered3: \(filtered3)")

extension Sequence {
    func filter(_ predicateFormat: String, _ args: Any...) -> [Element] {
        let predicate = NSPredicate(format: predicateFormat, argumentArray: args)
        return self.filter({ predicate.evaluate(with: $0) })
    }
}

// With an extension similar to what does Realm
let filtered4 = objects.filter("title == 'Title1'")
print("filtered4: \(filtered4)")

// With an extension similar to what does Realm, and less hard coded string (cf filtered3 construction)
let filtered5 = objects.filter("%K == %@", #keyPath(TestClass.title), searchedText)
print("filtered5: \(filtered5)")

With output:

$>filtered1: [TestClass - title: Title1]
$>filtered2: [TestClass - title: Title1]
$>filtered3: [TestClass - title: Title1]
$>filtered4: [TestClass - title: Title1]
$>filtered5: [TestClass - title: Title1]

CodePudding user response:

Chances are you are looking at a feature of "Realm", which I think is a tool for working with stored objects, and not a feature of Swift.

The question in this case is "What is realm?.objects(...) returning?". At a guess I would say it's not a String... it's some other object with a custom definition of filter that knows how to parse a String and apply it as a query for a data fetching operation.

This is Polymorphism on the method filter. It can do different things depending on the class of the entity it's called on.

  • Related