Reputation: 524
I have some data like the following:
NAME | LAST_NAME | PLACE
Roger - Martins - Miami
Mary - Rogers - Paris
Jack - Smith - Maryland
Alfred - Cooper - Germany
... and many more
And I have a predicate like this:
let searchPredicateName = NSPredicate(format: Database.Key.Name + " CONTAINS[cd] %@", _searchString)
let searchPredicateLastName = NSPredicate(format: Database.Key.LastName + " CONTAINS[cd] %@", _searchString)
let searchPredicatePlace = NSPredicate(format: Database.Key.Place + " CONTAINS[cd] %@", _searchString)
let finalPredicate = NSCompoundPredicate(orPredicateWithSubpredicates: [searchPredicateTitle, searchPredicateLastName, searchPredicatePlace])
When I search for example "Mar" then the results I would get will be:
Roger - Martins - Miami
Mary - Rogers - Paris
Jack - Smith - Maryland
The results are OK, but I would like to have Mary Rogers first. One option would be to fetch separately but then I wouldn't have one NSFetchedResultsController as a datasource and that would impact the performance
Upvotes: 3
Views: 352
Reputation: 758
After reviewing comments from hush-entangle above I can clearly see my answer is completely wrong. I was a little too excited to get started on this website.. You are actually searching for a string position, likely using a UISearchController yeah? In that case you want to sort your results ie:
func sortForString(_ string: String) -> [Person] {
let string = string.lowerCased()
// searchResultsArray or if you want all just sort the fetched objects
(fetchedResultsController.fetchedObjects?.sorted(by: { lhs, rhs in
personInfoString(lhs).range(of: string ).lowerBound < personInfoString(rhs).range(of: string ).lowerBound
})) ?? [Person]()
}
func personInfoString(_ person: Person) -> NSString {
(person.first ?? "").appending(person.last ?? "").appending(person.place ?? "").lowercased() as NSString
}
let sorted = sortForString("Mar")
Then display your sorted strings. Notice if the search string is not contained the original order remains.
cleaned up might be
extension Person {
func sortableString() -> NSString {
(self.first ?? " ").appending(self.last ?? " ").appending(self.place ?? " ").lowercased() as NSString
}
}
extension NSFetchedResultsController where ResultType == Person {
func sortedObjects(for searchString: String) -> [Person] {
(self.fetchedObjects?.sorted(by: { lhs, rhs in
lhs.sortableString().range(of: searchString ).lowerBound < rhs.sortableString().range(of: searchString ).lowerBound
})) ?? [Person]()
}
}
Upvotes: 1
Reputation: 817
I assume that you have some entity UserMO
with fields name
, lastName
and place
. Also, I assume that you are using the UISearchController
.
To sort by name, use the NSSortDescriptor
. We omit the predicate, since we initially want to display all the elements. Based on this, we will write a function for creating a controller:
func createFetchedResultsController() -> NSFetchedResultsController<UserMO> {
let request: NSFetchRequest<UserMO> = UserMO.fetchRequest()
request.sortDescriptors = [
NSSortDescriptor(keyPath: \UserMO.name, ascending: true)
]
return NSFetchedResultsController<UserMO>(
fetchRequest: request,
managedObjectContext: context,
sectionNameKeyPath: nil,
cacheName: nil)
}
At the moment when the user edits the search string, the delegate method updateSearchResults
will be called. There, depending on what the user entered, we edit the predicate. You don't need to change the sorting rules.
extension ViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let text = searchController.searchBar.text ?? ""
let predicate: NSPredicate?
if text.isEmpty {
predicate = nil // We want to display all items
} else {
predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [
NSPredicate(format: Database.Key.Name + " CONTAINS[cd] %@", text),
NSPredicate(format: Database.Key.LastName + " CONTAINS[cd] %@", text),
NSPredicate(format: Database.Key.Place + " CONTAINS[cd] %@", text),
])
}
fetchedResultsController.fetchRequest.predicate = predicate
do {
try fetchedResultsController.performFetch()
} catch {
print(error)
}
}
}
Upvotes: 0
Reputation: 758
I think what you're actually looking for is NSSortDescriptor
.
When you use NSFetchedResultsController
your NSFetchRequest
has to have a sort descriptor -- that's when you do the sorting.
NSPredicate
is for comparing. ie: What you want to find.
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "section", ascending: true),// sort by section key first
NSSortDescriptor(key: "Name", ascending: true), //continue sorting by first name
NSSortDescriptor(key: "LastName", ascending: true), //continue sorting by last name.
NSSortDescriptor(key: "Place", ascending: true)] //finally by place.
Upvotes: 1