Skip to content

Instantly share code, notes, and snippets.

@dneprDroid
Created April 23, 2018 09:08
Show Gist options
  • Save dneprDroid/94ef5b1808d6f7db46530f38930ac2f8 to your computer and use it in GitHub Desktop.
Save dneprDroid/94ef5b1808d6f7db46530f38930ac2f8 to your computer and use it in GitHub Desktop.
RxAutosuggest for RxSwift
import RxSwift
class RxAutosuggest<Model> {
//MARK: Types
typealias QueryCache = (items: [Model], query: String)
typealias Sort<T> = (T, T)->Bool
typealias SortGenerator = (_ query: String)->Sort<Model>
typealias Filter<T> = (T)->Bool
typealias FilterGenerator = (_ query: String)->Filter<Model>
typealias QueryResult = (items: [Model], query: String)
typealias NetworkRequestGenerator = (_ query:String)->Observable<[Model]>
typealias ShowItemsCallback = (_ items: [Model])->Void
typealias HideItemsCallback = ()->Void
var lastQuery:String = ""
var cachedResults:QueryCache = (items: [], query: "")
var filter:FilterGenerator
var sort:SortGenerator
var minAutosuggestTimeInterval:RxTimeInterval = 0.75
var minSymbolsCount = 3
init(filter: @escaping FilterGenerator,
sort: @escaping SortGenerator) {
self.filter = filter
self.sort = sort
}
func listen(textObservable: Observable<String>,
networkRequest: @escaping NetworkRequestGenerator,
showCallback: @escaping ShowItemsCallback,
hideCallback: @escaping HideItemsCallback)->Observable<QueryResult> {
let backgroundScheduler = ConcurrentDispatchQueueScheduler(qos: .background)
return textObservable
.debounce(minAutosuggestTimeInterval, scheduler: backgroundScheduler)
.filter { str in
let empty = str.count < self.minSymbolsCount
if empty {
DispatchQueue.main.async {
hideCallback()
}
}
return !empty
}
.flatMapLatest {[unowned self] query ->Observable<QueryResult> in
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
return self.fetchModelsObservable(from: networkRequest, query: query, callback: showCallback)
}
.filter { [unowned self] _, oldQuery in
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
return oldQuery == self.lastQuery || self.lastQuery.contains(oldQuery)
}
.observeOn(MainScheduler.instance)
}
private func fetchModelsObservable(from request: NetworkRequestGenerator,
query: String,
callback: @escaping ShowItemsCallback)->Observable<QueryResult> {
lastQuery = query
let networkRequest:Observable<QueryResult> = request(query)
.map { [unowned self] items->QueryResult in
self.cachedResults = (items: items, query: query)
return (items: items, query: query)
}
.catchErrorJustReturn(([], query))
let loadFromCache = query.contains(cachedResults.query)
if loadFromCache {
let cachedSorted:[Model] = cachedResults.items
.filter(filter(query))
.sorted(by: sort(query))
return Observable.just(cachedSorted)
.observeOn(MainScheduler.instance)
.flatMapLatest { (localItems:[Model])->Observable<QueryResult> in
callback(localItems)
return networkRequest.map { receivedModels, q in
return (items: receivedModels + cachedSorted, query: q)
}
}
}
return networkRequest
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment