This is generic logic for handling network requests that load data based on pages. It is assumed that the view controller would implement the UISource protocol. I have a test suite as well. I'm especially interested in any ideas for doing away with the Variable. I'm also interested in ideas for refactoring this code into smaller functions.import RxSwiftprotocol UISource {    var refreshTrigger: Observable<Void> { get } // reloads most recently loaded page    var nextPageTrigger: Observable<Void> { get } // loads next page    var bag: DisposeBag { get }}protocol NetworkSource {    associatedtype Item    func loadData(page: Int) -> Observable<[Item]>}enum Result<T> {    case success(T)    case failure(Error)}struct PaginationNetworkSink<T> {    let loading: Observable<Bool> // true if network loading is in progress.    let elements: Observable<[T]> // elements from all loaded pages    let error: Observable<Error> // fires once for each error}func paginationNetworkLogic<T, API>(ui: UISource, api: API) -> PaginationNetworkSink<T> where API: NetworkSource, T == API.Item {    let loadResults = Variable<[Int: [T]]>([:])    let refreshOffset = ui.refreshTrigger        .map { loadResults.value.keys.max() }        .filter { $0 != nil }        .map { $0! }    let nextOffset = ui.nextPageTrigger.map { () -> Int in        if let page = loadResults.value.keys.max() {            return page + 1        } else {            return 0        }    }    let pageToLoad = Observable.of(refreshOffset, nextOffset)        .merge()        .startWith(0)    let loadResult = pageToLoad.flatMapLatest {        api.loadData(page: $0)            .map { Result.success($0) }            .catchError { Observable.just(Result.failure($0)) }    }.share()    let successResult = loadResult.map { (result) -> [T] in        if case .success(let array) = result {            return array        } else {            return []        }    }    let pageAndResult = Observable.zip(pageToLoad, successResult) { (page: $0.0, array: $0.1) }        .filter { !$0.array.isEmpty }    pageAndResult.subscribe(onNext: {        loadResults.value[$0.page] = $0.array    }).disposed(by: ui.bag)    let startLoading = pageToLoad        .map { _ in true }    let loadingComplete = loadResult.map { _ in false }    let loading = Observable.of(startLoading, loadingComplete)        .merge()        .startWith(true)    let elements: Observable<[T]> = loadResults.asObservable().map { results in        var result: [T] = []        let keys = results.keys.sorted()        for key in keys {            result += results[key]!        }        return result    }.startWith([])    let error = loadResult.map { result -> Error? in        if case .failure(let error) = result {            return error        } else {            return nil        }    }.filter { $0 != nil }.map { $0! }    return PaginationNetworkSink(loading: loading, elements: elements, error: error)}
Paging loader using RxSwift
swift;pagination;reactive programming;rx swift