使用 RxSwift 链接登录操作

Posted

技术标签:

【中文标题】使用 RxSwift 链接登录操作【英文标题】:Chaining login operations with RxSwift 【发布时间】:2018-11-22 03:45:01 【问题描述】:

我正在创建一个具有特定双向身份验证过程的应用: 首先,带有凭据的基于 REST 的登录,它返回服务器上的 websocket 端点,以及用于连接到它的 authtoken。

只有在websocket成功连接到服务器后,我才应该切换到下一个视图。

尝试在 MVVM 下实现,我为服务器网络调用创建了一个 API 结构:

我的 LoginViewController 将用户名和密码绑定到 LoginViewModel,它反过来将登录 Action 绑定到登录按钮:

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


我在执行此功能时遇到问题。它应该首先调用 REST 登录 API,并在获得响应后,开始与 websocket 的连接。服务端 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】:

我认为“平面地图”是您正在寻找的。​​p>

看看:

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)

【讨论】:

这确实似乎是我正在寻找的方向,但是我的 onPrimaryLogin() 逻辑似乎有缺陷:无论我得到什么响应,视图都会改变从登录方法(目前正在测试它的错误处理)。我也会很感激这方面的指针(在 ViewModel 中测试返回的 Observable,以便将操作转换为屏幕转换或向用户发出警告提示),并且在任何情况下,一旦我接受答案确定它确实解决了我所有的问题。 我在此处添加了一个错误处理示例,希望对您有所帮助。 终于设法将它们捆绑在一起。我缺少的东西确实是 FlatMap,错误处理代码确实有帮助。

以上是关于使用 RxSwift 链接登录操作的主要内容,如果未能解决你的问题,请参考以下文章

RxSwift:链接流的问题

如何在 RxSwift 中使用 1 个按钮创建登录和注销?

Swift (RxSwift):使用泛型链接 ViewItem 和 Cell 类

RxSwift 链接请求

使用 RxSwift 触发的两个事件

在 RxSwift 中处理嵌套的 observables