我们是否总是需要在闭包内部调用完成处理程序?

时间:2019-01-15 17:57:38

标签: ios swift closures

我有一个完成处理程序在我的闭包内部被调用。但是,只有当一切顺利时,才会调用完成处理程序。如果发生错误,则不会调用完成处理程序。

func product(with id: String, _ completion: @escaping (Product) -> ()) {

     // Make a network request for the product
     ...

    if (product) {
       completion(product)
    }
}

这是一个不好的设计吗?我最近得到的评论是,即使发生错误也需要调用完成处理程序,否则调用方将无限期等待。我以前从未听说过,现在我想知道这是否适用于Swift。

3 个答案:

答案 0 :(得分:3)

Strictly spoken the caller doesn't wait at all. The code in the closure will or will not be executed.

However it's good practice to return errors, too.

A smart way is an enum, as generic you can use it for many different types

enum Result<T> {
    case success(T), failure(Error)
}


func product(with id: String, completion: @escaping (Result<Product>) -> Void) {

     // Make a network request for the product
     ...
    if error = error { completion(.failure(error)); return }

    if product {
       completion(.success(product))
    } else {
       let error = // create some other error
       completion(.failure(error))
    }
}

And call it

product(with: "Foo") { result in
   switch result {
     case .success(let product): // do something with the product
     case .failure(let error): // do something with the error
   }
}

Note: The underscore character before completion in the function declaration is pointless.

答案 1 :(得分:1)

If you will not call a completion nothing will happen 'cuz completion caller will not wait for it.

But if you want to cover all cases try to add a failure callback. For example:

func product(with id: String, _ success: @escaping (Product) -> (), failure: @escaping (Any) -> ())

答案 2 :(得分:1)

In your case, if you are treating it as a completion, it means that it has to get called no matter what's case (success of failure with error), it has to returns when the process completed.

What you could do is to pass an optional error and product to the completion closure and then check whether the error is nil or not:

func product(with id: String, _ completion: @escaping (Product?, Error?) -> ()) {
    // in case of there is an error:
    completion(nil, error)
    return

    // if things went happy:
    completion(product, nil)
}

Calling the method:

product(with: "ID") { (product, error) in
    guard let returnedError = error else {
        print(product)

        return
    }

    print(returnedError)
}

Or:

product(with: "ID") { (product, error) in
    if let returnedError = error {
        print(returnedError)
        return
    }

    print(product)
}