指定完成处理程序

时间:2016-02-20 13:08:29

标签: ios swift closures

我现在已经有过几次这个问题,所以我想我会伸手去拿。

我有许多负责进行异步网络调用的网络接口,每个接口都有大约5/6个函数,它们都使用具有相同定义的完成处理程序:

(success: Bool, resources: [String: AnyObject] -> Void)

我正在寻找替代方法将其添加到每个函数的末尾,因为它会强制每个函数声明到2/3行。例如

func performSomeNetworkCall(withThisParam parm1: AnyObject, param2: AnyObject, completion: (success: Bool, resources: [String: AnyObject] -> Void)) {

}

关于如何解决长声明问题并重写每个功能的完成定义,我有几个想法。
  

思考一

使用typealias来定义闭包,如下所示:

typealias CompletionHandler = (success: Bool, resources: [String: AnyObject] -> Void)?

理论上,这可以解决这两个问题,因为它可以快速编写并解决代码长度问题。但是,当从外部源调用函数时,typealias不会像常规闭包一样自动完成,这意味着您必须每次都写出来导致错误。


思考二

使用代码片段(当Xcode实际记得你设置它们时)。这在理论上也是有效的,只要处理代码的每个其他开发人员也具有相同的代码片段并且它知道它而不是编写整个声明。


思考三

我考虑将完成处理程序定义为函数并将函数作为参数传递,如下所示:

func completionHandler() -> (success: Bool, resources: [String: AnyObject] -> Void) {
        return completionHandler()
    }

但是,天堂已经能够实现我想要的目标。



修改

我希望通过思想三实现目标

func completionHandler() -> ((success: Bool, resources: [String: AnyObject]) -> Void) {
    return completionHandler()
}

func networkCall(completion: (success: Bool, resources: [String: AnyObject]) -> Void) {
    let request = NSURLRequest(URL: NSURL(string: "http://www.google.co.uk")!)

    let session = NSURLSession.sharedSession()

    session.dataTaskWithRequest(request) { (data, response, error) -> Void in
        let handler = completionHandler()
        handler(success: true, resources: [String: AnyObject]())
    }
}

func foo() {
    let handler = completionHandler()
    networkCall(handler)

    print(handler)
    // hope to use handler.success || handler.resources here
}

虽然目前只是停留在completionHandler方法调用上 - (指出明显的......)

2 个答案:

答案 0 :(得分:3)

typealias是典型的解决方案。即使在原始定义的文件之外,它也应该自动完成。我认为问题在于您对typealias的定义。如果你这样定义(注意括号而不是可选):

typealias CompletionHandler = (success: Bool, resources: [String: AnyObject]) -> Void

func performSomeNetworkCall(withThisParam parm1: AnyObject, param2: AnyObject, completion: CompletionHandler?) {
    // Implementation
}

然后自动完成它就像这样:

autocomplete for completion handler

点击返回键后:

expanded autocomplete for completion handler

答案 1 :(得分:1)

  

我正在寻找替代方案

恕我直言,写更简洁的代码的更好的选择将使用“承诺”或“期货”。这些还不是Swift标准库的一部分,但其他语言确实有一个或另一个实现 - 并且已经有Swift的第三方库。

例如,对于“Scala-like”Futures,您的代码可能如下所示:

func performSomeNetworkCall(
    withThisParam parm1: AnyObject, 
    param2: AnyObject) 
    -> Future<[String: AnyObject]>

因此,异步函数不是完成处理程序,而是返回 Future 。 future是一个泛型类,其类型参数等于计算值。如果底层异步函数失败,未来也可能表示错误。

虽然通过组合器功能注册了延续,但您获得了最终结果:

let future = performSomeNetworkCall(param1, param2: param2)
future.map { computedValue in
    print("Result: \(computedValue)")
}
.onFailure { error in
    print("Error: \(error)")
}

此处,map是一个组合器函数,我们可以使用它来注册 continuation 。组合器基于其延续的返回值返回新的未来,因此我们可以组合期货,执行更复杂的任务。

当未来成功完成时,将调用延续函数。它将传递底层异步函数的作为其参数。延续返回Void

另一个组合子是flatMap。与map相反,它的继续只会带来另一个未来:

login().flatMap { token in
    fetchUser(id: userId).map { user in 
        print("User: \(user)")
    }
}.onFailure { error in
    print("Error: \(error)")
}

函数login是一个异步函数。成功完成后,它会在其继续中调用异步函数fetchUser。当fetchUser成功完成时,它会打印获取的用户对象。

如果有任何失败,错误将传播到已注册onFailure的“失败处理程序”。