如何从Endpoint结构体属性传递Decodable.Protocol作为函数参数?
非泛型Endpoint传递Decodable类型到Alamofire的问题
我定义了如下Endpoint结构体,用于封装API请求的核心信息:
struct Endpoint { let responseDecodable: Decodable.Type let path: String let queryItems: [URLQueryItem] // 假设此处包含URL构建的计算属性逻辑 var url: URL? { return nil } }
通过Alamofire发起请求的函数实现如下:
func fetchData(_ endpoint: Endpoint) { AF.request(endpoint.url!).responseDecodable(of: endpoint.responseDecodable) { response in // 响应处理逻辑 } }
为不同API接口定义了Endpoint的静态构造方法:
extension Endpoint { static func getUser(by id: Int) -> Endpoint { return Endpoint( responseDecodable: UsersGet.self, path: "/users/\(id)", queryItems: [] ) } static func getFriends(by user: User) -> Endpoint { return Endpoint( responseDecodable: FriendsGet.self, path: "/users/\(user.id)/friends", queryItems: [] ) } }
当前遇到的问题:调用fetchData时触发报错 Cannot convert value of type 'Decodable.Type' to expected argument type 'T.Type',原因是Alamofire的responseDecodable(of:)要求传入具体的泛型类型,而非抽象的Decodable.Type。需要在不将Endpoint改为泛型结构体的前提下解决该类型传递问题。
解决方案
方案1:调整fetchData为泛型函数,调用时显式指定类型
修改fetchData为泛型函数,让调用者明确指定期望的解码类型,同时校验Endpoint存储的类型是否匹配:
func fetchData<T: Decodable>(_ endpoint: Endpoint, completion: @escaping (Result<T, Error>) -> Void) { // 校验类型一致性,避免类型不兼容问题 guard endpoint.responseDecodable == T.self else { let mismatchError = NSError( domain: "APIError", code: 1001, userInfo: [NSLocalizedDescriptionKey: "Endpoint响应类型与期望类型不匹配"] ) completion(.failure(mismatchError)) return } AF.request(endpoint.url!) .responseDecodable(of: T.self) { response in completion(response.result) } }
调用示例:
// 获取用户信息 fetchData(Endpoint.getUser(by: 1)) { (result: Result<UsersGet, Error>) in switch result { case .success(let user): // 处理UsersGet类型数据 print(user) case .failure(let error): print(error.localizedDescription) } } // 获取好友列表 fetchData(Endpoint.getFriends(by: currentUser)) { (result: Result<FriendsGet, Error>) in switch result { case .success(let friends): // 处理FriendsGet类型数据 print(friends) case .failure(let error): print(error.localizedDescription) } }
方案2:将请求解码逻辑封装到Endpoint内部
给Endpoint扩展一个请求执行方法,直接处理请求和解码,回调返回Decodable类型,调用时再做类型转换:
extension Endpoint { func performRequest(completion: @escaping (Result<Decodable, Error>) -> Void) { guard let url = url else { let invalidURLError = NSError( domain: "APIError", code: 1000, userInfo: [NSLocalizedDescriptionKey: "无效的请求URL"] ) completion(.failure(invalidURLError)) return } AF.request(url) .responseData { response in switch response.result { case .success(let data): do { let decodedObject = try JSONDecoder().decode(self.responseDecodable, from: data) completion(.success(decodedObject)) } catch { completion(.failure(error)) } case .failure(let error): completion(.failure(error)) } } } }
调用示例:
Endpoint.getUser(by: 1).performRequest { result in switch result { case .success(let object): if let user = object as? UsersGet { // 处理用户数据 print(user) } case .failure(let error): print(error.localizedDescription) } }
方案3:使用Alamofire底层的_responseDecodable方法
Alamofire提供了底层的_responseDecodable方法,可直接接受Decodable.Type参数,绕过泛型限制:
func fetchData(_ endpoint: Endpoint, completion: @escaping (Result<Decodable, Error>) -> Void) { guard let url = endpoint.url else { let invalidURLError = NSError( domain: "APIError", code: 1000, userInfo: [NSLocalizedDescriptionKey: "无效的请求URL"] ) completion(.failure(invalidURLError)) return } AF.request(url) ._responseDecodable(decoder: JSONDecoder(), responseSerializer: DecodableResponseSerializer(type: endpoint.responseDecodable)) { response in completion(response.result) } }
调用时需在回调中做类型转换,逻辑同方案2。
内容的提问来源于stack exchange,提问作者PigeonPO
相关产品推荐
相关产品推荐

