You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Guard捕获URLSession网络错误?附API请求代码

解决URLSession网络请求的崩溃与错误捕获问题

首先,你的代码里有几个导致崩溃和错误未妥善处理的地方:

  • 强制解包myUrl!:如果URL字符串格式错误,这行直接崩溃
  • 网络错误仅做return:像-1005这类连接错误没有被捕获并反馈给用户
  • 强制解包data!:如果网络请求返回空数据,这里也会崩溃

咱们可以通过guard语句和完善的错误处理逻辑来解决这些问题,同时统一错误提示的方式:

第一步:定义自定义错误类型(可选但更清晰)

先定义一个枚举来统一管理所有可能的错误,让错误处理更规范:

enum MedicationLoadError: Error {
    case invalidURL
    case networkError(String)
    case missingData
    case missingField(String) // 用于标记具体缺失的字段,比如"name"、"drugs"
}

第二步:修改loadMedication函数的错误处理

把所有强制解包换成guard捕获,同时在网络回调里妥善处理各类错误,并回到主线程更新UI(网络任务在后台线程,不能直接操作UI):

func loadMedication(){
    print("Getting medication")
    self.medication.removeAll()
    
    // 1. 用guard捕获无效URL,避免强制解包崩溃
    guard let myUrl = URL(string: "URL HIDDEN") else {
        print("无效的请求URL")
        DispatchQueue.main.async {
            self.loaded = false
            // 这里可以添加用户提示,比如弹出alert或更新错误提示标签
        }
        return
    }
    
    var request = URLRequest(url: myUrl)
    request.httpMethod = "POST"
    let postString = "userToken=" + User.user.getUserId()
    request.httpBody = postString.data(using: .utf8)
    
    let task = URLSession.shared.dataTask(with: request) { [weak self] (data, response, error) in
        guard let self = self else { return }
        
        // 2. 处理网络请求错误(比如连接失败、URL错误等)
        if let error = error {
            print("网络请求失败:\(error.localizedDescription)")
            DispatchQueue.main.async {
                self.loaded = false
                // 提示用户:"无法连接服务器,请检查网络状态"
            }
            return
        }
        
        // 3. 捕获空数据的情况,避免data!强制解包崩溃
        guard let responseData = data else {
            print("请求返回空数据")
            DispatchQueue.main.async {
                self.loaded = false
                // 提示用户:"数据获取失败,请稍后重试"
            }
            return
        }
        
        do {
            // 4. 安全解析JSON,避免强制解包
            guard let json = try JSONSerialization.jsonObject(with: responseData, options: []) as? [[String: Any]] else {
                throw MedicationLoadError.missingData
            }
            
            for dayData in json {
                guard let mName = dayData["name"] as? String else {
                    throw MedicationLoadError.missingField("药物名称")
                }
                guard let drugs = dayData["drugs"] as? String else {
                    throw MedicationLoadError.missingField("药物成分")
                }
                guard let id = dayData["id"] as? String else {
                    throw MedicationLoadError.missingField("ID")
                }
                guard let dateTime = dayData["dateTime"] as? String else {
                    throw MedicationLoadError.missingField("服用时间")
                }
                guard let quantity = dayData["quantity"] as? String else {
                    throw MedicationLoadError.missingField("服用剂量")
                }
                
                self.medication.append(
                    Medication(
                        name: mName,
                        drugs: drugs,
                        id: id,
                        administeredOn: dateTime,
                        quantity: quantity
                    )
                )
            }
            
            // 解析成功,回到主线程更新状态
            DispatchQueue.main.async {
                self.loaded = true
                print("Medication loaded")
            }
            
        } catch let error as MedicationLoadError {
            // 处理自定义错误,给出对应提示
            var errorMessage = ""
            switch error {
            case .missingData:
                errorMessage = "数据格式错误"
            case .missingField(let field):
                errorMessage = "\(field)字段缺失"
            default:
                errorMessage = "未知错误"
            }
            print(errorMessage)
            DispatchQueue.main.async {
                self.loaded = false
                // 提示用户对应的错误信息
            }
        } catch {
            // 处理其他未知错误
            print("解析JSON失败:\(error.localizedDescription)")
            DispatchQueue.main.async {
                self.loaded = false
                // 提示用户:"数据解析失败,请稍后重试"
            }
        }
    }
    task.resume()
}

关键修改点说明:

  • 安全创建URL:用guard let替代myUrl!,避免无效URL导致的崩溃,同时给出错误提示
  • 网络错误处理:不再忽略error,像-1005这类连接错误会被捕获并反馈给用户
  • 空数据检查:用guard let responseData = data确保数据存在,避免data!强制解包崩溃
  • 主线程更新:所有UI相关操作(设置loaded状态、提示用户)都放到DispatchQueue.main.async里,符合iOS线程规则
  • 自定义错误类型:让错误信息更清晰,方便后续扩展和统一处理

这样修改后,无论是URL无效、网络连接失败、数据空还是字段缺失,都能被妥善捕获,并给用户对应的错误提示,不会再出现崩溃的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:51:53