Swift4/iOS开发:如何将协议传入Completion Handler实现泛型JSON解析
通用Swift网络请求+泛型JSON解析方案
早上好呀Stack Overflow社区的小伙伴!很高兴能帮你解决这个Swift网络编程里的泛型解析问题~你想要实现的通用网络请求逻辑完全可行,核心就是利用泛型约束和Result类型来完美适配任意Decodable对象,同时避开协议传参带来的坑。
核心思路
你之前遇到的问题,大概率是直接把Decodable协议类型传给了Completion Handler——这会因为Swift的协议类型擦除,导致JSONDecoder无法确定具体要解析的对象类型。而用泛型的话,编译器会在调用时锁定具体的模型类型,就能让解析正常工作了。
完整实现代码
先写一个通用的网络请求函数,它接受任意遵循Decodable的泛型类型,并用Result类型统一处理成功/失败的回调:
import Foundation func fetchDecodableData<T: Decodable>(from url: URL, completion: @escaping (Result<T, Error>) -> Void) { // 发起URLSession请求 URLSession.shared.dataTask(with: url) { data, response, error in // 优先处理请求错误 if let requestError = error { completion(.failure(requestError)) return } // 校验是否获取到有效数据 guard let responseData = data else { let noDataError = NSError(domain: "NetworkError", code: 1001, userInfo: [ NSLocalizedDescriptionKey: "未获取到响应数据" ]) completion(.failure(noDataError)) return } // 尝试解析JSON do { let decodedObject = try JSONDecoder().decode(T.self, from: responseData) completion(.success(decodedObject)) } catch let decodingError { completion(.failure(decodingError)) } }.resume() // 别忘了启动任务! }
如何复用这个通用函数
假设你有一个自定义的User模型,遵循Decodable:
struct User: Decodable { let id: Int let username: String let email: String }
调用通用请求函数时,Swift会自动推断泛型类型,你也可以显式指定:
// 替换成你的实际接口地址 guard let userUrl = URL(string: "https://api.example.com/users/1") else { print("无效的URL") return } // 发起请求,Swift会自动推断T是User类型 fetchDecodableData(from: userUrl) { result in switch result { case .success(let user): print("成功获取用户:\(user.username),邮箱:\(user.email)") case .failure(let error): print("请求失败:\(error.localizedDescription)") } }
如果要解析其他模型(比如Post),只需要定义好遵循Decodable的Post结构体,直接复用这个函数就行,完全不需要修改请求逻辑。
关键点说明
- 泛型约束
<T: Decodable>:确保传入的类型必须符合Decodable协议,让JSONDecoder能正确匹配模型结构。 Result<T, Error>类型:统一处理成功和失败的回调,比传统的(T?, Error?)更清晰,避免出现两者都为nil或都有值的混乱情况。- 避开协议类型直接传参:泛型在编译时会确定具体的类型,解决了直接用
Decodable作为参数时的类型擦除问题,让解析器能准确完成工作。
这样你就拥有了一个可以无限复用的通用网络请求工具,不管是解析用户、文章还是其他任何自定义模型,都能直接套用啦~
内容的提问来源于stack exchange,提问作者dewclaw
相关产品推荐
相关产品推荐

