Skip to content

Instantly share code, notes, and snippets.

@hslatman
Created October 16, 2018 21:53
Show Gist options
  • Save hslatman/c32ae4771142603e9ca293dd774efce1 to your computer and use it in GitHub Desktop.
Save hslatman/c32ae4771142603e9ca293dd774efce1 to your computer and use it in GitHub Desktop.
A generic loader for any sort of data. Just fill out the inputs.
struct LoadInput<T, U> {
let trigger: Observable<T> /// will cause the network request to start passing the T to the request generator.
let makeRequest: (T) -> URLRequest /// a function that knows how to create the URLRequest
let download: (URLRequest) -> Observable<Data> /// the function that actually does the download. By default, it uses URLSession.shared
let makeObject: (Data) -> U /// a function that knows how to convert the data into the needed resource.
}
extension LoadInput {
init(trigger: Observable<T>, makeRequest: @escaping (T) -> URLRequest, makeObject: @escaping (Data) -> U) {
self.trigger = trigger
self.makeRequest = makeRequest
self.download = URLSession.shared.rx.data(request:)
self.makeObject = makeObject
}
}
struct NetworkLoader<T, U> {
let item: Observable<U>
let error: Observable<Error>
let refreshing: Observable<Bool>
init(input: LoadInput<T, U>) {
let trigger = input.trigger
let networkData = input.trigger
.map(input.makeRequest)
.flatMap { input.download($0).materialize() }
.share(replay: 1, scope: .whileConnected)
item = networkData
.map { $0.element }
.filter { $0 != nil }
.map { $0! }
.map(input.makeObject)
error = networkData
.map { $0.error }
.filter { $0 != nil }
.map { $0! }
// The delay is necessary because calling `endRefreshing()` on a
// UIRefreshControl too quickly after beginning the refresh
// will cause it to not work.
refreshing = Observable.merge([
trigger.map { _ in true },
networkData.delay(0.125, scheduler: MainScheduler.instance)
.map { _ in false }
])
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment