Объединение операций входа в систему с помощью RxSwift - PullRequest
0 голосов
/ 12 июня 2018

Я создаю приложение, которое имеет определенный процесс двусторонней аутентификации: во-первых, вход в систему на основе REST с учетными данными, который возвращает конечную точку веб-сокета на сервере, а также авторизационный токен, который используется для подключения к нему.

Только после успешного подключения веб-сокета к серверу, я должен переключиться на следующее представление.

Пытаясь реализовать в MVVM, я создал структуру API для сетевых вызовов сервера:

Мой LoginViewController привязывает имя пользователя и пароль к LoginViewModel, который в свою очередь привязывает действие входа в систему к кнопке входа в систему:

func onLogin() -> CocoaAction {
    return CocoaAction { _ in
        self.loginService.login(username: self.usernameText.value, password: self.passwordText.value)
        let MainViewModel = MainViewModel(sceneCoordinator: self.sceneCoordinator)
        return self.sceneCoordinator.transition(to: Scene.mainView(mainViewModel), type: .modal).asObservable().map { _ in }
    }
}

Служба LoginService должна вернуть Completable для входа в систему, чтобычтобы указать успешный вход в систему (и переместить представление на главный экран приложения) или ошибку, чтобы показать пользователю.

protocol LoginServiceType {

@discardableResult
func login(username: String, password: String) -> Completable

}

У меня возникла проблема с реализацией этой функции.Сначала он должен вызвать API входа в REST, а после получения ответа запустить соединение с веб-сокетом.Реализация API сервера выглядит следующим образом (в соответствии с рекомендуемыми примерами RxSwift MVVM):

struct Server: ServerProtocol {

// MARK: - API Errors
enum Errors: Error {
    case requestFailed
}

// MARK: - API Endpoint requests
static func login(for username: String, password: String) -> Observable<JSONObject> {
    let parameters = [
        "username": username,
        "password": password
    ]
    return request(address: Server.Address.login, parameters: parameters)
}

// MARK: - generic request to API
static private func request<T: Any>(address: Address, parameters: [String: String] = [:]) -> Observable<T> {
    return Observable.create { observer in
        let request = Alamofire.request(address.url.absoluteString,
                                        method: .post,
                                        parameters: parameters,
                                        encoding: JSONEncoding.default,
                                        headers: nil)
        request.responseJSON { response in
            guard response.result.isSuccess == true, let data = response.data,
                let json = try? JSONSerialization.jsonObject(with: data, options: []) as? T, let result = json else {
                    observer.onError(Errors.requestFailed)
                    return
            }
            observer.onNext(result)
            observer.onCompleted()
        }
        return Disposables.create {
            request.cancel()
        }
    }
}

}

Поэтому я пытаюсь выяснить, как подключить вызов REST, его ответс нужной конечной точкой + токеном для создания Websocket и подписки на обратный вызов подключения, который затем должен вернуть Completable обратно в LoginViewModel.

Любой совет будет приветствоваться.

1 Ответ

0 голосов
/ 12 июня 2018

Я думаю, что "плоская карта" - это то, что вы ищете.

Посмотрите:

var mynum = Variable(0)
let disposeBag = DisposeBag()

func login() -> Observable<(String,String)> {

    return Observable.create { observer in

        // Place your server access code

        if (<some_condition>) { // if error
            observer.on(.error(<custome error>))
        }

        observer.on(.next(("websocket", "authtoken")))
        observer.on(.completed)
        return Disposables.create()
    }
}

func API(webSiteData: (String, String)) -> Observable<Int> {

    return Observable.create { observer in

        // Place your second server access code

        print (webSiteData.0)
        print (webSiteData.1)

        observer.on(.next(1)) // assiging "1" just as an example, you may ignore
        observer.on(.completed)
        return Disposables.create()
    }
}

func combine() {
    self.login().catchError({ (currentError) -> Observable<(String, String)> in
        print (currentError.localizedDescription)
        return Observable.just(("",""))
    })
        .flatMap(self.API)
        .bind(to: mynum)
        .disposed(by: self.disposeBag)
}
...