如何在 Swift 上同步或使 Observable 等待?

Posted

技术标签:

【中文标题】如何在 Swift 上同步或使 Observable 等待?【英文标题】:How to Synchronize or make an Observable wait on Swift? 【发布时间】:2019-01-15 20:46:23 【问题描述】:

所以,我有这个登录功能,我想返回一个布尔值到我的 .xib 控制器,它必须根据登录成功进行一些修改。

 func login(cpf: String) -> Bool 
    let url = URL(string: AccessibilityDataAccessProvider.kLoginURL)
    let params = ["username": String.init(describing: cpf)]

    var success = false

    dataAccessProvider.postSimpleResponse(url: url!, params: params)
        .subscribe(onNext:  (userToken) in
            let userData = UserDefaults.standard
            userData.set(userToken, forKey: AccessibilityDataAccessProvider.userTokenKey)
            userData.synchronize()
            success = true
    , onError:  (error) in
        print("Login Error: \(error)")
    ).disposed(by: disposeBag)

    return success

postSimpleResponse() 方法的返回是 Any? 类型的 Observable

问题是,我的 login() 方法在 success 布尔变量在订阅中设置为 true 之前返回。

我该如何解决这个问题?

【问题讨论】:

RX 的全部意义在于您使用 Observables 对异步任务进行建模。如果您退出可观察的“世界”,那么您就失去了使用它的大部分目的。正确的做法是发出 var loginState: Observable<LoginState>,并订阅它以在有更改时更新您的 UI。 注意:这不是该问题的重复。如果您使用 RX,完成处理程序不是异步数据建模的正确解决方案。 【参考方案1】:

您可以将 Observable 映射到 Bool observable(虽然这里不需要 Bool,您可以使用 Void),并从 login 方法返回 Observable

像这样:

func login(cpf: String) -> Observable<Bool> 
    let url = URL(string: AccessibilityDataAccessProvider.kLoginURL)
    let params = ["username": String.init(describing: cpf)]

    return dataAccessProvider.postSimpleResponse(url: url!, params: params)
        .do(onNext: 
            let userData = UserDefaults.standard
            userData.set(userToken, forKey: AccessibilityDataAccessProvider.userTokenKey)
        )
        .map  _ in
            return true
    

然后观察它:

login(cpf: "data").subscribe(onNext:  _ in
    // Success
, onError:  _ in
    // Failure
).disposed(by: disposeBag)

【讨论】:

【参考方案2】:

如前所述:

Rx 的全部意义在于您使用 Observables 对异步任务进行建模。

但是如果你真的需要让 observable 同步(等到onNext,或者onCompleted),你可以使用DispatchGroup (monitor)。

您的代码应如下所示:

func login(cpf: String) -> Bool 
    let url = URL(string: AccessibilityDataAccessProvider.kLoginURL)
    let params = ["username": String.init(describing: cpf)]

    var result = false

    let group = DispatchGroup()
    group.enter()

    let subscription = dataAccessProvider.postSimpleResponse(url: url!, params: params)
        .subscribe(onNext: 
            let userData = UserDefaults.standard
            userData.set(userToken, forKey: AccessibilityDataAccessProvider.userTokenKey)

            result = true
            group.leave()
        , onError:  _ in 
            group.leave()
        )

    group.wait() // wait till leave() method called
    subscription.dispose()

    return result

您应该记住,这种方法会阻塞当前线程,直到异步代码的执行完成。因此,它并不完美,但有时可能是必要的。

【讨论】:

以上是关于如何在 Swift 上同步或使 Observable 等待?的主要内容,如果未能解决你的问题,请参考以下文章

如何在附加到 TextField 的 swift @Published didSet 中正确格式化字符串?

如何增加文本的笔画或使图像中的文本加粗?在 Python 中

Swift 3 Core Data - 如何同步保存上下文?

如何正确使用信号灯同步 Alamofire 请求 Swift4 的主线程?

如何突出显示 DataGridView 行或使其暂时发光?

如何在 Swift 中同步检索 PFUser objectForKey