Arnaud
Arnaud

Reputation: 955

Realm Swift background search

I have a realm database in my app, containing a list of ~2000 users.

A tableview displays these users, and a search bar allows to filter them (on 6 different properties of each user). This operation was blocking the UI, so I put it in a background thread.

Now it's a lot better, but I'm not 100% sure that it's the best way to do this.

Can you suggest any other solutions, if you have any better ?

Here's the sample code I use :

func filterUsers(searchText:String,completion: (result: Array<User>) -> ()){

    var IIDS = Array<String>()

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in

        let predicate1 = NSPredicate(format: "firstName contains[c] %@", searchText)
        let predicate2 = NSPredicate(format: "lastName contains[c] %@", searchText)

        let bgRealm = try! Realm()
        bgRealm.beginWrite()

        //Filter the whole list of users
        var results = Array(bgRealm.objects(User)).filter {
            //One user object by one
            let usr:User = $0
            //Reset the value by default
            usr.searchResultField = ""

            if predicate1.evaluateWithObject(usr) {
                usr.searchResultField = "firstNameORlastName"
                return true
            }
            else if predicate2.evaluateWithObject(usr) {
                usr.searchResultField = "IID"
            }

            return false
        };

        try! bgRealm.commitWrite()

        for usr in results {
            IIDS.append("'\(usr.IID)'")
        }

        results.removeAll()

        dispatch_async(dispatch_get_main_queue(), { () -> Void in

            let realm = try! Realm()
            let foundUsers = Array(realm.objects(User).filter("IID IN {\(IIDS.joinWithSeparator(","))}"))
            IIDS.removeAll()
            completion(result: foundUsers)
        })
    })
}

Upvotes: 2

Views: 1047

Answers (1)

marius
marius

Reputation: 7806

You filter the objects after pulling them all into memory (by converting the Results to an Array). You'll have a vastly better performance, if you let Realm handle the filtering. For that purpose you'd need to be able to make all your queries by predicates which you can combine to one OR-compound predicate.

Furthermore I'd avoid storing the matching field in the object to separate concerns as the values are transient. They are only needed as long those objects are kept in memory.

Beside that I'd recommend to use a primary key for IID and then just retrieve one object by another instead of building a huge predicate with all IDs.

To put it all together, this would be the way I'd tackle that:

func filterUsers(searchText:String, completion: (result: Array<(user: User, field: String)>) -> ()) {   
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        var predicates = [
          "firstName": NSPredicate(format: "firstName contains[c] %@", searchText)
          "lastName":  NSPredicate(format: "lastName contains[c] %@", searchText)
        ]
        let compoundPredicate = NSCompoundPredicate(orPredicateWithSubpredicates: Array(predicates.values))

        let bgRealm = try! Realm()

        // Filter the whole list of users
        let results = bgRealm.objects(User).filter(compoundPredicate)

        // Find out which field is matching
        let idsAndFields: [(IID: String, field: String)] = results.flatMap {
            for (field, predicate) in predicates {
                if predicate.evaluateWithObject($0) {
                    return (IID: $0.IID, field: field)
                }
            }
            return nil
        }

        dispatch_async(dispatch_get_main_queue()) {
            let realm = try! Realm()
            let result = idsAndFields.flatMap {
                if let user = realm.objectForPrimaryKey($0.IID) {
                    return (user: user, field: $0.field)
                } else {
                    return nil
                }
            }
            completion(result: result)
        })
    }
}

Upvotes: 2

Related Questions