Всегда ли нам нужно вызывать обработчик завершения внутри замыкания? - PullRequest
0 голосов
/ 15 января 2019

У меня есть обработчик завершения, который вызывается внутри моего замыкания. Однако обработчик завершения вызывается только тогда, когда все идет хорошо. В случае ошибки обработчик завершения никогда не вызывается.

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

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

    if (product) {
       completion(product)
    }
}

Это плохой дизайн? Недавно я получил комментарий, что обработчики завершения должны вызываться даже в случае ошибок, в противном случае вызывающая сторона будет ждать бесконечно. Я никогда не слышал этого раньше, и теперь мне интересно, относится ли это к Swift.

Ответы [ 3 ]

0 голосов
/ 15 января 2019

В вашем случае, если вы рассматриваете его как завершение , это означает, что его нужно вызывать независимо от того, что происходит (успешное завершение с ошибкой), оно должно возвращаться после завершения процесса .

Что вы могли бы сделать, это передать необязательные error и product закрытию завершения, а затем проверить, является ли ошибка nil или нет:

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)
}

Вызов метода:

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

        return
    }

    print(returnedError)
}

Или:

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

    print(product)
}
0 голосов
/ 15 января 2019

Строго говоря, звонящий вообще не ждет. Код в закрытии будет или не будет выполнен.

Однако рекомендуется также возвращать ошибки.

Умный способ - enum, так как универсальный вы можете использовать его для многих различных типов

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))
    }
}

И назовите это

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
   }
}

Примечание. Символ подчеркивания перед completion в объявлении функции не имеет смысла.

0 голосов
/ 15 января 2019

Если вы не будете вызывать завершение, ничего не произойдет, потому что вызывающий завершение не будет ждать его.

Но если вы хотите охватить все случаи, попробуйте добавить обратный вызов сбоя. Например:

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