简体   繁体   English

使用rxSwift进行长轮询

[英]LongPolling With rxSwift

I studying rxSwift, and I want to do service for the interaction of c longpolling server to this service imitating a permanent connection. 我正在研究rxSwift,我想为c longpolling服务器与该服务的交互提供服务,以模仿永久连接。 I wrote it, but it seems to me, is not that the decision could have been done better? 我写了它,但是在我看来,是不是该决定可以做得更好? Is it possible to somehow repeat the Observable, regardless of the error, and depending on longpoll server response. 是否可能以某种方式重复Observable,而不考虑错误,并取决于longpoll服务器的响应。

Can anyone can share the solution? 任何人都可以分享解决方案吗? Or help with advice? 或提供建议? How it is better to organize? 如何更好地组织? I would like to see a better solution, since only began studying rxswift 我想看到一个更好的解决方案,因为它只是开始研究rxswift

class LongPollingService {

    public var messageReciver: PublishSubject<EventProtocol> = PublishSubject<EventProtocol>()

    private let transport = DefaultTransport()

    private let disposeBag = DisposeBag()

    private var currentRequestInfo = Variable<LongpollingServerInfo?>(nil)

    private var currentRequestDisposable: Disposable?

    private var currentLongpollingConnection: Disposable? // Subsribee for request server info

    private var eventListener : Disposable?

    private var currentReqursiveConnection: Disposable? // Subscriber for event listener from longpoll server

    func startObservableEvents() {
        getServerConnection()
        subscribeServerInfo()
        //testing listen events
        eventListener = messageReciver.showMessagesInDebugMode().subscribe()
        eventListener?.addDisposableTo(disposeBag)
    }

    func disconnect() {
        currentRequestDisposable?.dispose()
        currentLongpollingConnection?.dispose()
        currentReqursiveConnection?.dispose()
    }

    private func subscribeServerInfo() {
        currentLongpollingConnection = currentRequestInfo
            .asObservable()
            .filter({$0 != nil})
            .subscribe(onNext: { [weak self] (info) in
                guard let sSelf = self else { return }
                sSelf.subscribeToEvents(timeStamp: info!.ts)
            })
        currentLongpollingConnection?.addDisposableTo(disposeBag)
    }

    private func subscribeToEvents(timeStamp: TimeInterval) {
        if let serverInfo = currentRequestInfo.value {
            currentReqursiveConnection?.dispose()
            currentReqursiveConnection = getEventsFromLongpollServer(serverInfo: serverInfo, with: timeStamp)
                .flatMap(parseUpdates)
                .flatMap(reciveEvents)
                .showErrorsSwiftMessagesInDebugMode()
                .subscribe(onNext: { [weak self] updates in
                    guard let sSelf = self else { return }
                    sSelf.subscribeToEvents(timeStamp: updates)
                },
                onError: { [weak self] error in
                    guard let sSelf = self else { return }
                        if let error = error as? LongPollError {
                            switch error {
                            case .olderHistory(let ts): sSelf.subscribeToEvents(timeStamp: ts)
                            default: sSelf.getServerConnection()
                            }
                        }
                })
            currentReqursiveConnection?.addDisposableTo(disposeBag)
        }
    }

    private func getServerConnection() {
        //get longpolling server info for connection.
        currentRequestDisposable = getLongpollServerInfo()
            .subscribe(onNext: {[weak self] info in
                guard let sSelf = self else { return }
                sSelf.currentRequestInfo.value = info
            })
        currentRequestDisposable?.addDisposableTo(disposeBag)
    }

    private func parseUpdates(json: Any) throws -> Observable<LongPollingUpdates> {
        let response = try Mapper<LongPollingUpdates>().map(JSONObject: json)
        return .just(response)
    }

    private func reciveEvents(updates:LongPollingUpdates) throws -> Observable<TimeInterval> {
        if let errors = updates.failed {
            throw parseErrors(errors: errors)
        }
        if let events = updates.updates {
            parseUpdates(updates: events)
        }
        return Observable.just(updates.timeStamp!)
    }

    private func parseUpdates(updates: [[Any]]) {
        updates.forEach { (array) in
            let firstElementInUpdate = array.first
            if let update = firstElementInUpdate as? Int {
                switch update {
                case 1: break
                case 2: break
                case 3: break
                case 4: messageReciver.onNext(NewMessage(array: array))
                default: break
                }
            }
        }
    }

    private func parseErrors(errors: [String: Any]) -> LongPollError {
        if let error = errors["failed"] as? Int {
            switch error {
            case 1:
                guard let ts = errors["ts"] as? TimeInterval else { return .unkownError }
                return .olderHistory(ts: ts)
            case 2: return .needNewkey
            case 3: return .needCaseAndTs
            case 4: return .unkownVersion
            default:
                return .unkownError
            }
        }
        return .unkownError
    }

    private func getEventsFromLongpollServer(serverInfo: LongpollingServerInfo, with ts: TimeInterval) -> Observable<Any> {
        let url = buildLongPollingServerRoute(from: serverInfo, with: ts)
        let request = buldLongPollRequst(route: url)
        let requestConvert = try? URLEncoding.default.encode(request!, with: nil)
        return transport.makeRequest(request: requestConvert!)
    }

    private func getEventsFromLongpollServer(serverInfo: LongpollingServerInfo) -> Observable<Any> {
        let url = buildLongPollingServerRoute(from: serverInfo)
        let request = buldLongPollRequst(route: url)
        let requestConvert = try? URLEncoding.default.encode(request!, with: nil)
        return transport.makeRequest(request: requestConvert!)
    }

    private func getLongpollServerInfo() -> Observable<LongpollingServerInfo> {
        let request = MessageRouter.getLongpollServer(useSsl: false, needPts: false)
        return transport.makeModel(request: request)
    }

}

So assuming you have a function like: 因此,假设您具有以下功能:

func getData() -> Observable<Data>

And you want to long poll it at a specific period , you can do something like this: 而且您想在特定period对其进行长时间轮询,可以执行以下操作:

Observable<Int>.interval(period, scheduler: MainScheduler.instance)
  .map { _ in return }
  .flatMap(getData)
  .subscribe( /* ... handle data ... */)
  .disposed(by: disposeBag)

You can use other schedulers than MainScheduler if that is more appropriate. 如果更合适,则可以使用MainScheduler以外的其他调度程序。

Now if you want also handle Error s that getData might emit and you don't want that to necessarily unsubscribe the long polling, then you can do this: 现在,如果您还希望处理getData可能发出的Error ,并且不想让该订阅退订长轮询,则可以执行以下操作:

func handleError(error: Error) -> Observable<Data> {
  return Observable.empty()
}

Observable<Int>.interval(period, scheduler: MainScheduler.instance)
  .map { _ in return }
  .flatMap { return getData.catchError(handleError) }
  .subscribe( /* ... handle data ... */)
  .disposed(by: disposeBag)

You can also analyze the error in handleError and decide if you want to continue by emitting an empty Observable or cancel the long polling by emitting another error. 您还可以分析handleError的错误,并通过发出一个空的Observable来决定是继续进行操作,还是通过发出另一个错误来取消长时间轮询。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM