Last active
June 15, 2022 17:14
-
-
Save romyilano/ac8379a28f66e767d7dc5ec313f34ad7 to your computer and use it in GitHub Desktop.
Various search bar implementations. Including an RxSwift / reactive swift version too
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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 | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You nailed it with rx example, thanks a lot!