如何用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
相关产品推荐
相关产品推荐

