I have a big array of alimentobject named baseOuChercheAliments. I have to search in it with the searchbar. when i do that, with the code below, the table view display the result of the search of the first letter tapped, and not all the words tapped. In fact, the search of the list of the first letter takes time and this is what is displayed in the end and not the search of what is tapped totaly in the search bar.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
DispatchQueue.global(qos: .default).async { [self] in
let searchTextBeingQuireid = searchText
var searchedRecords = [AlimentObject]()
let words = Set(searchText.split(separator: " ").map(String.init))
if searchText.isEmpty {
searchedRecords = [] //baseOuChercheAliments
} else {
var baseDeDonneesAlimentsFiltreeClassique = baseOuChercheAliments.filter{$0.nomAliment.range(of: searchText, options: .anchored) != nil}
baseDeDonneesAlimentsFiltreeClassique.sort(by: { $0.nomAliment < $1.nomAliment})
var baseDeDonneesAlimentsFiltreeComplexe = baseOuChercheAliments.filter { object in words.allSatisfy { word in object.nomAliment.localizedCaseInsensitiveContains(word) } }
baseDeDonneesAlimentsFiltreeComplexe.sort(by: { $0.nomAliment < $1.nomAliment })
var soustraction = Array(Set(baseDeDonneesAlimentsFiltreeComplexe).subtracting(baseDeDonneesAlimentsFiltreeClassique))
soustraction.sort(by: { $0.nomAliment > $1.nomAliment })
searchedRecords = baseDeDonneesAlimentsFiltreeClassique + soustraction
}
if searchText == searchTextBeingQuireid {
DispatchQueue.main.async {
baseDeDonneesAlimentsFiltree = searchedRecords
self.tableView.reloadData()
}
}
}
}
Could someone tell me how to fix that in order to display the result of what is totally tapped in the search bar and not only the result of the search of the first letter tapped in the searchbar.
Blocks capturing function arguments by value, that's why when you check at the end of the filtration searchText == searchTextBeingQuireid
it'll always be true.
Instead, you need to store current searchText
in your class, and compare with it during filtration.
As you need to stop outdated filtration as fast as you can, you need to check if searchText
was changed after each long operation. I've added checks between each operation, but it may be redundant, you can only leave checks after operations which take time(can check it with prints)
private var searchText = ""
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
self.searchText = searchText
DispatchQueue.global(qos: .default).async { [self] in
var searchedRecords = [AlimentObject]()
let words = Set(searchText.split(separator: " ").map(String.init))
if searchText.isEmpty {
searchedRecords = [] //baseOuChercheAliments
} else {
var baseDeDonneesAlimentsFiltreeClassique = baseOuChercheAliments.filter{$0.nomAliment.range(of: searchText, options: .anchored) != nil}
if self.searchText != searchText {
return
}
baseDeDonneesAlimentsFiltreeClassique.sort(by: { $0.nomAliment < $1.nomAliment})
if self.searchText != searchText {
return
}
var baseDeDonneesAlimentsFiltreeComplexe = baseOuChercheAliments.filter { object in words.allSatisfy { word in object.nomAliment.localizedCaseInsensitiveContains(word) } }
if self.searchText != searchText {
return
}
baseDeDonneesAlimentsFiltreeComplexe.sort(by: { $0.nomAliment < $1.nomAliment })
if self.searchText != searchText {
return
}
var soustraction = Array(Set(baseDeDonneesAlimentsFiltreeComplexe).subtracting(baseDeDonneesAlimentsFiltreeClassique))
if self.searchText != searchText {
return
}
soustraction.sort(by: { $0.nomAliment > $1.nomAliment })
if self.searchText != searchText {
return
}
searchedRecords = baseDeDonneesAlimentsFiltreeClassique + soustraction
}
if self.searchText == searchText {
DispatchQueue.main.async {
baseDeDonneesAlimentsFiltree = searchedRecords
self.tableView.reloadData()
}
}
}
}