RxSwift分页重复请求

时间:2018-12-04 14:37:19

标签: swift rx-swift

重复的分页请求:

-只需从RXExample https://github.com/ReactiveX/RxSwift/tree/master/RxExample/RxExample/Examples/GitHubSearchRepositories中删除searchBar

-滚动到底部的“ performSearch”时,彼此调用了两次。

预期结果:

滚动到底部仅一次调用“ performSearch”

更新: 移除remove heightForRowAt时解决了此问题。从视图控制器

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 90
    }

我不知道为什么,但是它在起作用

let tableView: UITableView = self.tableView
    let loadNextPageTrigger: (Driver<GitHubSearchState>) -> Signal<()> =  { state in
        tableView.rx.contentOffset.asDriver()
            .withLatestFrom(state)
            .flatMap { state in
                return tableView.isNearBottomEdge(edgeOffset: 20.0) && !state.shouldLoadNextPage
                    ? Signal.just(())
                    : Signal.empty()
        }
    }

    let activityIndicator = ActivityIndicator()
    let state = githubSearchRepositories(
        loadNextPageTrigger: loadNextPageTrigger,
        performSearch: { endCursor in
            GitHubApiV4.sharedAPI.loadSearchURL(endCursor)
                .debug()
                .trackActivity(activityIndicator)
    })

enum GitHubCommand {
  case loadMoreItems
  case gitHubResponseReceived(SearchResponse)
}

struct GitHubSearchState {
  // control
  var shouldLoadNextPage: Bool
  var userList: Version<[User]> // Version is an optimization. When something unrelated changes, we don't want to reload table view.
  var failure: GitHubServiceError?
  var endCursor: String
  init() {
    shouldLoadNextPage = true
    userList = Version([])
    failure = nil
    endCursor = "Y3Vyc29yOjE="
  }
}

此方法包含分页GitHub搜索的要点。

func githubSearchRepositories(
loadNextPageTrigger: @escaping (Driver<GitHubSearchState>) -> Signal<()>,
performSearch: @escaping (String) -> Observable<SearchResponse>
) -> Driver<GitHubSearchState> {

let searchPerformerFeedback: (Driver<GitHubSearchState>) -> Signal<GitHubCommand> = react(
    query: { (state) in
        GithubQuery(endCursor: state.endCursor, shouldLoadNextPage: state.shouldLoadNextPage)
},
    effects: { query -> Signal<GitHubCommand> in
        if !query.shouldLoadNextPage {
            return Signal.empty()
        }

        if query.endCursor.isEmpty {
            return Signal.just(GitHubCommand.gitHubResponseReceived(.success((users: [], endCursor: ""))))
        }

        return performSearch(query.endCursor)
            .asSignal(onErrorJustReturn: .failure(GitHubServiceError.networkError))
            .map(GitHubCommand.gitHubResponseReceived)
})

// this is degenerated feedback loop that doesn't depend on output state
let inputFeedbackLoop: (Driver<GitHubSearchState>) -> Signal<GitHubCommand> = { state in
    let loadNextPage = loadNextPageTrigger(state).map { _ in GitHubCommand.loadMoreItems }
    return Signal.merge(loadNextPage)
}

// Create a system with two feedback loops that drive the system
// * one that tries to load new pages when necessary
// * one that sends commands from user input
return Driver.system(
    initialState: GitHubSearchState.initial,
    reduce: GitHubSearchState.reduce,
    feedback: searchPerformerFeedback, inputFeedbackLoop
)
}

extension GitHubSearchState {
var isOffline: Bool {
    guard let failure = self.failure else {
        return false
    }

    if case .offline = failure {
        return true
    }
    else {
        return false
    }
}

var isLimitExceeded: Bool {
    guard let failure = self.failure else {
        return false
    }

    if case .githubLimitReached = failure {
        return true
    }
    else {
        return false
    }
}
}

extension GitHubSearchState: Mutable {}

Api

public func loadSearchURL(_ endCursor: String) -> Observable<SearchResponse> {
    let urlRequest = generateRequest(cursor: endCursor)
    return URLSession.shared
        .rx.response(request: urlRequest)
        .retry(3)
        .observeOn(Dependencies.sharedDependencies.backgroundWorkScheduler)
        .map { pair -> SearchResponse in
            if pair.0.statusCode == 403 {
                return .failure(.githubLimitReached)
            }

            if !(200 ..< 300 ~= pair.0.statusCode) {
                throw exampleError("Call failed")
            }

            let jsonRoot = JSON(pair.1)

            guard let search = jsonRoot["data"]["search"].dictionary else {
                throw exampleError("Casting to dictionary failed")
            }

            guard let nodes = search["nodes"]?.array else {
                throw exampleError("Casting to array failed")
            }

            var userList = [User]()

            for node in nodes {
                userList.append(User(dict: node))
            }

            let endCursor = search["pageInfo"]?["endCursor"].string ?? ""

            return .success((users: userList, endCursor: endCursor))
    }.retryOnBecomesReachable(.failure(.offline), reachabilityService: _reachabilityService)
}

0 个答案:

没有答案