Search code examples
iosswiftrealm

Swift 4: Can't get NSPredicate working with the searchBar and a local Realm


I've been trying to solve this problem for the last hours. I just don't understand why it doesn't work.

Summary: I want to filter a local realm by searchBar input. If I set the predicate hardcoded it just works fine, if I pass the searchBar.text into the predicate format mit results are nil.

✔ Working (hard coded string):

func updateSearchResults(for searchController: UISearchController) {

    let manualString = "Steven"

    results = results.filter("ID >1 AND name CONTAINS %@", manualString)
    print(results.first)
    tableView.reloadData()

}

Console:

6:53:01.262 ViewController.updateSearchResults(): - Optional(colleagues {
    ID = 40;
    quoteText = Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.;
    name = Steven;
    UUID = 3ad52f7e-ad2e-46c4-aace-079c0eb8db17;
    timestamp = 2018-10-24 14:09:16 +0000;
})

Output works, I get my realm object and the tableView shows the result of my filtered realm by predicate.

Working

Now I insert the searchBar.text into my predicate:

❌ NOT Working (searchBar string):

func updateSearchResults(for searchController: UISearchController) {

    results = results.filter("ID >1 AND name CONTAINS %@", searchController.searchBar.text!)

    print(searchController.searchBar.text!)
    print(results.first)
    tableView.reloadData()

}

Console:

16:15:31.615 ViewController.updateSearchResults() searchBar.text: - Steven

16:15:31.615 ViewController.updateSearchResults() result: - nil

Nothing

Nothing!

I don't get it, i even checked the type of both variants with (type(of: T). Both is rendered as string.

Thanks a lot in advance!





Additional Information:

ViewController.swift (unnecessary shortened):

class Cell: UITableViewCell {
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String!) {
        super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
    }

    required init(coder: NSCoder) {
        fatalError("NSCoding not supported")
    }
}

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    var results = realm.objects(colleagues.self).sorted(byKeyPath: "timestamp", ascending: false)
    var notificationToken: NotificationToken?

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.delegate = self
        tableView.dataSource = self

        searchController.searchResultsUpdater = self
        tableView.tableHeaderView = searchController.searchBar

        setupViews()

        // Set results notification block
        self.notificationToken = results.observe { (changes: RealmCollectionChange) in
            switch changes {
            ...
            }
        }
    }

    let tableView: UITableView = {
        let tableView = UITableView()
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.register(Cell.self, forCellReuseIdentifier: "cell")

        return tableView
    }()


    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return results.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! Cell

        let object ...

        return cell
    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            realm.beginWrite()
            realm.delete(results[indexPath.row])
            try! realm.commitWrite()
        }
    }

    let searchController = UISearchController(searchResultsController: nil)


    fileprivate func setupViews(){
        ... 
    }
}

extension ViewController: UISearchResultsUpdating {

    func updateSearchResults(for searchController: UISearchController) {

        results = results.filter("ID >1 AND name CONTAINS %@", searchController.searchBar.text!)

        print(searchController.searchBar.text!)
        print(results.first)
        tableView.reloadData()
    }
}

Solution

  • I think I have found the reason why it did not work, was a logic problem of mine:

    let searchStr = String(format: "%@", searchController.searchBar.text!)
    let newResults = results.filter("ID >1 AND author CONTAINS %@", searchStr)
    
    • As soon as I tapp the searchBar, the variable results gets cleared:
      1. Result will be filtered with "" (empty)
      2. The now empty filtered results overwrites the initial filled result (results = results.filter(...)

    If I transfer the filtered result into a new result it works!