首页 > 解决方案 > 如何避免我没有编写的 swift 函数的回调地狱?

问题描述

我不希望这被误解为重复。我想处理来自 Firestore(Firebase 的数据库,一个 Google 平台)的 API 调用引起的回调地狱。我不能修改他们的函数声明,所以我假设我必须用我的一些代码来包装他们的函数。

例如,在下面的代码中,该函数eventCreatedSuccessfully()只能在异步函数完成后调用。eventCreatedSuccessfully()还包含一个对具有闭包的firebase的函数调用,另一个函数依赖它等等......虽然这现在不会给我带来问题,但它可能会随着我的应用程序变得越来越大。我在线研究并从第三方框架中找到了诸如 Futures 和 Streams 之类的解决方案,但我不知道如何将它们集成到我无法控制的代码中(API 调用)。

batch.commit { (error) in
    self.dismiss(animated: true) {
        if error == nil {
            self.eventCreatedSuccessfully()
            print("Event created successfully")
        } else {
            print(error!.localizedDescription)
        }
    }
}

标签: iosswiftfirebasecallbackgoogle-cloud-firestore

解决方案


将调用包装在 Promise 中。任何流行的库都可以解决问题。想到的是 PromiseKit,在https://github.com/mxcl/PromiseKit上可用(在撰写本文时) 。

这是我为一个工作项目(它是开源的)编写的代码,它包装了一个接受完成的函数,并返回一个 Promise,它会在调用完成时用结果发出信号。它使用内部 Promise 实现,但该过程可以适应其他实现。

public func promise<Return>(_ task: (@escaping (Return?, Error?) -> ()) -> ()) -> Promise<Return> {
    let p = Promise<Return>()

    task { (value: Return?, error: Error?) -> Void in
        if let error = error {
            p.signal(error)
        }

        if let value = value {
            p.signal(value)
        }
    }

    return p
}

预计将使用某种结果或错误调用完成。根据您的用例的需要进行调整。

下面是一个示例用法。

public typealias BalanceCompletion = (Balance?, Error?) -> Void

func balance(completion: @escaping BalanceCompletion) {
    guard deleted == false else {
        completion(nil, KinError.accountDeleted)

        return
    }

    Stellar.balance(account: stellarAccount.publicKey!, asset: asset, node: node)
        .then { balance -> Void in
            completion(balance, nil)
        }
        .error { error in
            completion(nil, KinError.balanceQueryFailed(error))
    }
}

func balance() -> Promise<Balance> {
    return promise(balance)
}

推荐阅读