You need to enable JavaScript to run this app. 如何在Swift网络操作工作流中添加异常抛出的错误处理
优惠活动
大模型
产品
解决方案
定价
更多

如何在Swift网络操作工作流中添加异常抛出的错误处理

回调式异步接口的错误处理方案

核心问题说明

你当前使用的是闭包回调式的异步接口,这类接口无法直接使用Swift同步throw语法抛出错误——异步代码的执行栈和调用方已经分离,throw的错误无法传递到外层的异步上下文。
你现有代码的核心问题是:所有校验/编码失败的分支都直接return,没有主动调用completion闭包,导致调用方永远收不到错误回调,甚至出现回调卡死的情况。

具体修改方案

1. 细化错误枚举,明确错误类型

先完善你定义的ContentServiceError,对应不同的失败场景,方便调用方精准处理:

enum ContentServiceError: Error, LocalizedError {
  case idIsEmpty
  case urlInvalid
  case encodingError(underlying: Error)
  
  // 可选:补充错误描述,便于调试
  var errorDescription: String? {
    switch self {
    case .idIsEmpty: return "传入的内容ID为空"
    case .urlInvalid: return "请求URL构造失败"
    case .encodingError(let underlying): return "内容编码失败:\(underlying.localizedDescription)"
    }
  }
}

2. 改造编码函数,改用同步throws抛出错误

把原来返回可选Data?的contentData函数改为同步throws版本,移除返回可选的隐式错误标记,同时修正原函数参数标签和调用处不匹配的问题:

private func contentData(from content: Content) throws -> Data {
  let encoder = JSONEncoder()
  // 编码失败直接抛出底层错误,由上层捕获
  return try encoder.encode(content)
}

3. 改造主接口,所有异常分支必须调用completion

把原来嵌套的guard拆成独立校验分支,每个失败分支都主动调用completion传入对应错误,成功分支正常发起网络请求:

func postContent(
    id: String?,
    content: String,
    completion: @escaping(Error?) -> ()
  ) {
    // 校验ID合法性
    guard let validID = id else {
      completion(ContentServiceError.idIsEmpty)
      return
    }
    // 校验URL合法性
    guard let url = URL(string: "\(baseURL)\(contentEndPoint)\(validID)") else {
      completion(ContentServiceError.urlInvalid)
      return
    }
    // 捕获编码阶段抛出的错误
    let contentData: Data
    do {
      contentData = try contentData(from: Content(id: validID, content: content))
    } catch {
      completion(ContentServiceError.encodingError(underlying: error))
      return
    }
    // 所有校验通过后发起网络请求
    let request = Request(headers: nil, httpMethod: .post, httpBody: contentData, parameters: nil, url: url)
    let networkHandler = NetworkHandler(urlSession: URLSession(configuration: URLSessionConfiguration.ephemeral))
    networkHandler.dataRequest(request, successHandler: { data in
      completion(nil)
    }, failureHandler: { error in
      completion(error)
    })
  }

关于throw的使用说明

你代码里标注的两处「// how to add throw here」,在当前回调式异步接口里无法直接使用throw。throw仅适用于同步函数的错误抛出,调用方需要在同一个执行栈内用do-catch捕获。
如果需要使用throw语法,可以把现有回调接口封装为async/await的异步函数,示例如下:

func postContent(id: String?, content: String) async throws {
  try await withCheckedThrowingContinuation { continuation in
    postContent(id: id, content: content) { error in
      if let error = error {
        continuation.resume(throwing: error)
      } else {
        continuation.resume()
      }
    }
  }
}

封装后调用方就可以直接用do-catch处理错误:

DispatchQueue.global(qos: .background).async {
  do {
    try await postContent(id: id, content: content)
    await MainActor.run { /* 成功回调,切回主线程处理UI */ }
  } catch {
    await MainActor.run { /* 错误处理,切回主线程提示用户 */ }
  }
}

内容的提问来源于stack exchange,提问作者konya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.30 04:24:03