如何处理完成闭包中的多个错误

时间:2017-05-23 10:49:46

标签: swift closures

我正在实现一个函数,它接受一个数组和一个完成闭包。我遍历数组中的项目,根据每个项目执行网络请求。每个请求完成后,它会将结果附加到集合中。

一旦所有请求都返回,就会使用累积的集合调用函数的完成块。

我的问题是,我不确定如何处理网络请求可能提供的(可能是多个)错误。有没有一种标准的方法来处理这种模式?想到的第一个想法是:

  • 一旦发出网络请求错误,请使用nil结果和错误调用函数的完成闭包。

  • 在所有网络请求全部完成后,使用某种结构调用完成闭包,该结构包含成功完成的调用的结果以及没有调用的错误。

这些选项似乎都不理想。有没有一种标准的方法来处理这个问题?我不确定这个功能的用户会合理期望什么。

我在Swift 3.1中实现了这一点。

1 个答案:

答案 0 :(得分:1)

我不知道你的完成闭包看起来如何,但你可以考虑使用enum为结果(here you can read more about associated values)使用相关值。

如果您创建enum,请执行以下操作:

enum ClosureResult {
   case success([String])
   case failure([String], [Error])
}

您可以根据网络查找失败还是成功返回不同的值。请注意,我将Error个对象数组作为我failure案例中的第二个参数传递,我不知道这是否适合您,但这只是为了说明您可以传递的超过一个参数作为回报。

如果您使用ClosureResult作为完成闭包的返回值,则在返回时可以在自己的块中对其执行switch,并根据值执行操作。

所以,你的功能看起来像这样:

func yourFunction(items: [String], completion: (ClosureResult) -> Void) {
    var completedItems = [String]()
    var failedItems = [Error]()
    for item in items {
        //Do your network magic here 
        //if it succeeds add it to completedItems
        //if it fails, add it to failedItems
    }

    //did we encounter any failures?
    if failedItems.count > 0 {
        //yes we did, return a .failure then
        let failure = ClosureResult.failure(completedItems, failedItems)
        completion(failure)
    } else {
        //no...return .success
        let success = ClosureResult.success(completedItems)
        completion(success)
    }
}

然后你可以这样使用它:

let items = ["a", "b", "c"]
yourFunction(items: items) { result in
    switch result {
    case .success(let okItems):
        print(okItems)
    case .failure(let okItems, let failedItems):
        print(failedItems)
    }
} 

更新

在评论中你问:

  

我唯一担心的是,在您的示例中,网络请求在“a”和“c”而不是“b”上成功,闭包结果包含有关哪个项目失败的任何信息。在ClosureResult对象中返回项目的元组和该项目的网络结果而不仅仅是字符串是否合理?

是的,这是有道理的。我只使用StringError作为示例,但您也可以使用自己的类。

我认为,我要做的是,而不是使用元组,我会创建一个简单的类并返回它。

例如:

struct ClosureError {
    let item: String
    let error: Error
}

然后你的ClosureResult枚举可以使用:

enum ClosureResult {
   case success([String])
   case failure([String], [ClosureError])
}

yourFunction中,当您遇到失败时,您需要创建ClosureError的新实例:

    ...
    var failedItems = [ClosureError]()
    ...
    for item in items {
        //Do your network magic here 
        //if it succeeds add it to completedItems
        //if it fails, add it to failedItems
        let error = //create the error
        let closureFailure = ClosureFailure(item, error)
        failedItems.append(closureFailure) 
    }
    ...

最后,在您的switch中,您会知道哪些项目失败了:

let items = ["a", "b", "c"]
yourFunction(items: items) { result in
    switch result {
    case .success(let okItems):
        print(okItems)
    case .failure(let okItems, let failedItems):
        for closureError in failedItems {
            print("item: \(closureError.item) has failed with error \(closureError.error)"
        }
    }
} 

你应该为各个部分找出一些更好的名字:)

希望有意义并且可以使用。