简体   繁体   中英

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. 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.

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

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:

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.

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:

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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