Skip to content

Generic RxSwift operator to easily interact with paginated APIs

License

Notifications You must be signed in to change notification settings

fabfelici/RxPaginationFeedback

Repository files navigation

RxPaginationFeedback

pod Swift Package Manager compatible Build Status codecov

Generic RxSwift operator to easily interact with paginated APIs. Based on RxFeedback.

Design

public typealias PageProvider<PageDependency, Element> = (PageDependency) -> Observable<Page<PageDependency, Element>>

public static func paginationSystem<PageDependency: Equatable, Element>(
    scheduler: ImmediateSchedulerType,
    initialDependency: PageDependency,
    loadNext: Observable<Void>,
    pageProvider: @escaping PageProvider<PageDependency, Element>
) -> Observable<PaginationState<PageDependency, Element>>

Features

  • Simple state machine to represent pagination use cases.
  • Reusable pagination logic. No need to duplicate state across different screens with paginated apis.
  • Observe state to react to loading event, latest error and changes on the list of elements.

Examples

Simple Reqres example

struct ReqresResponse: Decodable {
    let data: [User]
}

struct User: Decodable {
    let firstName: String
    let lastName: String
}

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

let state = Driver.paginationSystem(
    initialDependency: 0,
    loadNext: loadNext
) { page -> Observable<PageResponse<Int, User>> in

    let urlRequest = URLRequest(url: URL(string: "https://reqres.in/api/users?page=\(page)")!)

    return URLSession.shared.rx.data(request: urlRequest)
        .compactMap {
            (try? decoder.decode(ReqresResponse.self, from: $0).data)
                .map {
                    Page(nextDependency: page + 1, elements: $0)
                }
        }
}

state.map { $0.elements }
    .drive(tableView.rx.items(cellIdentifier: "Cell", cellType: UITableViewCell.self)) { index, item, cell in
        cell.textLabel?.text = "\(item.firstName) - \(item.lastName)"
    }
    .disposed(by: disposeBag)

More examples