Skip to content

Instantly share code, notes, and snippets.

@romyilano
Last active June 15, 2022 17:14
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save romyilano/ac8379a28f66e767d7dc5ec313f34ad7 to your computer and use it in GitHub Desktop.
Save romyilano/ac8379a28f66e767d7dc5ec313f34ad7 to your computer and use it in GitHub Desktop.
Various search bar implementations. Including an RxSwift / reactive swift version too
//https://developer.apple.com/library/content/samplecode/TableSearch_UISearchController/Introduction/Intro.html
// MARK: - UISearchResultsUpdating
func updateSearchResults(for searchController: UISearchController) {
// Update the filtered array based on the search text.
let searchResults = products
// Strip out all the leading and trailing spaces.
let whitespaceCharacterSet = CharacterSet.whitespaces
let strippedString = searchController.searchBar.text!.trimmingCharacters(in: whitespaceCharacterSet)
let searchItems = strippedString.components(separatedBy: " ") as [String]
// Build all the "AND" expressions for each value in the searchString.
let andMatchPredicates: [NSPredicate] = searchItems.map { searchString in
// Each searchString creates an OR predicate for: name, yearIntroduced, introPrice.
//
// Example if searchItems contains "iphone 599 2007":
// name CONTAINS[c] "iphone"
// name CONTAINS[c] "599", yearIntroduced ==[c] 599, introPrice ==[c] 599
// name CONTAINS[c] "2007", yearIntroduced ==[c] 2007, introPrice ==[c] 2007
//
var searchItemsPredicate = [NSPredicate]()
// Below we use NSExpression represent expressions in our predicates.
// NSPredicate is made up of smaller, atomic parts: two NSExpressions (a left-hand value and a right-hand value).
// Name field matching.
let titleExpression = NSExpression(forKeyPath: "title")
let searchStringExpression = NSExpression(forConstantValue: searchString)
let titleSearchComparisonPredicate = NSComparisonPredicate(leftExpression: titleExpression, rightExpression: searchStringExpression, modifier: .direct, type: .contains, options: .caseInsensitive)
searchItemsPredicate.append(titleSearchComparisonPredicate)
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .none
numberFormatter.formatterBehavior = .default
let targetNumber = numberFormatter.number(from: searchString)
// `searchString` may fail to convert to a number.
if targetNumber != nil {
// Use `targetNumberExpression` in both the following predicates.
let targetNumberExpression = NSExpression(forConstantValue: targetNumber!)
// `yearIntroduced` field matching.
let yearIntroducedExpression = NSExpression(forKeyPath: "yearIntroduced")
let yearIntroducedPredicate = NSComparisonPredicate(leftExpression: yearIntroducedExpression, rightExpression: targetNumberExpression, modifier: .direct, type: .equalTo, options: .caseInsensitive)
searchItemsPredicate.append(yearIntroducedPredicate)
// `price` field matching.
let lhs = NSExpression(forKeyPath: "introPrice")
let finalPredicate = NSComparisonPredicate(leftExpression: lhs, rightExpression: targetNumberExpression, modifier: .direct, type: .equalTo, options: .caseInsensitive)
searchItemsPredicate.append(finalPredicate)
}
// Add this OR predicate to our master AND predicate.
let orMatchPredicate = NSCompoundPredicate(orPredicateWithSubpredicates:searchItemsPredicate)
return orMatchPredicate
}
// https://www.thedroidsonroids.com/blog/ios/rxswift-by-examples-1-the-basics/
// wow that's pretty clean
searchBar
.rx.text // Observable property thanks to RxCocoa
.orEmpty // Make it non-optional
.debounce(0.5, scheduler: MainScheduler.instance) // Wait 0.5 for changes.
.distinctUntilChanged() // If they didn't occur, check if the new value is the same as old.
.filter { !$0.isEmpty } // If the new value is really new, filter for non-empty query.
.subscribe(onNext: { [unowned self] query in // Here we subscribe to every new value, that is not empty (thanks to filter above).
self.shownCities = self.allCities.filter { $0.hasPrefix(query) } // We now do our "API Request" to find cities.
self.tableView.reloadData() // And reload table view data.
})
.addDisposableTo(disposeBag)
@ruslangazizov
Copy link

You nailed it with rx example, thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment