捕获模式更改回调签名

Catch pattern changes callback signature

我正在尝试使用 JSONDecoder 解码来自我的服务器的 json 响应,使用 Alamofire。当我用 guard 解码响应时,它可以正常工作。这种方法的副作用是当解码实际上失败时我无法判断问题是什么。

guard let result: TResponseData = try? decoder.decode(TResponseData.self, from: response.data!) else {
    self.logger.error("Unable to decode the response data into a model representation.")
    return
}

所以我想使用 do { } catch { } 但我无法弄清楚我应该如何在 Alamofire responseJSON 回调中使用它。

这是我目前得到的:

Alamofire.request(completeUrl, method: .post, parameters: parameters, encoding: encoding, headers: headers)
.validate()
 .responseJSON { (response) -> Void in
    self.logger.info("POST Response: \(String(describing:response.response?.statusCode))")
    switch response.result {
    case .success(_):
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .custom(Date.toTMDBDate)

        do {
            let _ = try decoder.decode(TResponseData.self, from: response.data!)
        } catch DecodingError.dataCorrupted(let error) {
            self.logger.error(error.underlyingError)
            return
        }

        completion(result)
        return
    case .failure(let error):
      //....
    }

然而,我得到的这段代码是 .responseJSON { (response) -> Void in 行的编译器错误。

Invalid conversion from throwing function of type '(_) -> Void' to non-throwing function type '(DataResponse) -> Void'.

保护代码工作正常,如果我将 try 更改为 try? 或强制解包,它会编译 - 我只是无法让我的 catch 处理实际错误.

如果我更改 catch 块使其不包含任何模式,则代码会编译。

catch {
    return
}

guard 给我的东西相比,这并没有给我带来任何好处。我真的很想捕获 decode 操作遇到的错误。我使用了错误的模式吗?为什么使用 DecodingError.dataCorrupted 模式似乎会更改回调签名?

JSONDecoder 可以抛出 DecodingError.dataCorrupted 以外的错误;您需要能够处理 arbitrary Error 被抛出的情况。所以,如果你想处理那个错误,你需要一个无条件的 catch {} 块。

您还可以:

  • 使用 responseData 而不是 responseJSON,因为您正在使用 JSONDecoder 进行自己的反序列化。
  • 在 Alamofire 的 Result 类型上使用 unwrap() 方法,以便在需要时合并网络错误和解码错误。

这就是它的样子:

Alamofire
    .request(
        completeUrl, method: .post, parameters: parameters,
        encoding: encoding, headers: headers
    )
    .validate()
    .responseData { response in

        self.logger.info(
            "POST Response: \(response.response?.statusCode as Any)"
        )

        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .custom(Date.toTMDBDate)

        do {
            let result = try decoder.decode(
                TResponseData.self, from: response.result.unwrap()
            )
            completion(result)
        } catch {
            self.logger.error(error)
        }
    } 

尽管这里要注意的一件事是,如果请求失败,您不会调用 completion;我会亲自改变你这样做,并通过让 completion 采用 Result<TResponseData> 参数来传播错误。

在这种情况下,您可以使用 ResultflatMap(_:) 方法而不是 unwrap()catch {} 块:

func doRequest(_ completion: @escaping (Result<TResponseData>) -> Void) {

    let completeURL = // ...
    let parameters =  // ...
    let encoding =    // ...
    let headers =     // ...

    Alamofire
        .request(
            completeURL, method: .post, parameters: parameters,
            encoding: encoding, headers: headers
        )
        .validate()
        .responseData { response in

            self.logger.info(
                "POST Response: \(response.response?.statusCode as Any)"
            )

            let decoder = JSONDecoder()
            decoder.dateDecodingStrategy = .custom(Date.toTMDBDate)

            // if response.result is successful data, try to decode.
            // if decoding fails, result is that error.
            // if response.result is failure, result is that error.
            let result = response.result.flatMap {
                try decoder.decode(TResponseData.self, from: [=11=])
            }
            .ifFailure {
                self.logger.error([=11=])
            }

            completion(result)
        }
}