Reputation: 955
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
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