Created
April 23, 2018 09:08
-
-
Save dneprDroid/94ef5b1808d6f7db46530f38930ac2f8 to your computer and use it in GitHub Desktop.
RxAutosuggest for RxSwift
This file contains 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
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