如何为Alamofire请求编写泛型包装?改造POST/GET为泛型函数
如何在Swift中为Alamofire编写泛型请求包装
我来一步步帮你把现有的Alamofire请求改造成灵活的泛型函数,这样就能轻松适配不同的请求参数和响应模型了。首先咱们得理清几个核心思路:用泛型覆盖请求参数和响应模型类型,用Swift的Codable协议替代手动拼字典和SwiftyJSON(更符合现代Swift规范),同时用Result类型让回调逻辑更清晰。
第一步:重构基础类型与工具
首先替换原来的CompletionHandler,改用泛型的Result回调,这样能传递成功的模型或具体错误:
import Foundation import Alamofire // 泛型回调类型:支持任意Decodable响应模型和Error typealias GenericCompletionHandler<T: Decodable> = (Result<T, Error>) -> Void // 保留你原来的String扩展(用于传递raw JSON字符串body的场景) extension String: ParameterEncoding { public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { var request = try urlRequest.asURLRequest() request.httpBody = data(using: .utf8, allowLossyConversion: false) return request } }
第二步:编写通用泛型请求核心方法
咱们先封装一个通用的performRequest方法,把Alamofire的重复逻辑抽离出来,支持任意HTTP方法、请求参数和响应模型:
public class ConnectionManager { // 假设你原来的BASE_URL和HEADER是全局常量,这里保留 private let baseURL = BASE_URL private let defaultHeaders = HEADER // 核心泛型请求方法 private func performRequest<T: Decodable, U: Encodable>( url: String, method: HTTPMethod, parameters: U?, encoder: ParameterEncoder, headers: HTTPHeaders?, completion: @escaping GenericCompletionHandler<T> ) { AF.request( url, method: method, parameters: parameters, encoder: encoder, headers: headers ).responseDecodable(of: T.self) { response in switch response.result { case .success(let model): completion(.success(model)) case .failure(let error): // 可以在这里统一处理状态码(比如400、500等) if let statusCode = response.response?.statusCode { print("请求失败,状态码:\(statusCode)") } completion(.failure(error)) } } } }
第三步:封装泛型POST/GET方法
基于核心方法,咱们快速实现泛型的POST和GET,适配不同的编码方式(POST用JSON编码,GET用URL编码):
extension ConnectionManager { // 泛型POST请求:支持任意Encodable参数和Decodable响应 func post<T: Decodable, U: Encodable>( url: String? = nil, parameters: U, headers: HTTPHeaders? = nil, completion: @escaping GenericCompletionHandler<T> ) { performRequest( url: url ?? baseURL, method: .post, parameters: parameters, encoder: JSONParameterEncoder.default, headers: headers ?? defaultHeaders, completion: completion ) } // 泛型GET请求:参数会自动拼接到URL上 func get<T: Decodable, U: Encodable>( url: String? = nil, parameters: U, headers: HTTPHeaders? = nil, completion: @escaping GenericCompletionHandler<T> ) { performRequest( url: url ?? baseURL, method: .get, parameters: parameters, encoder: URLEncodedFormParameterEncoder.default, headers: headers ?? defaultHeaders, completion: completion ) } // 额外:支持传递raw JSON字符串的POST请求(复用你原来的String扩展) func postRawString<T: Decodable>( url: String? = nil, rawBody: String, headers: HTTPHeaders? = nil, completion: @escaping GenericCompletionHandler<T> ) { AF.request( url ?? baseURL, method: .post, parameters: nil, encoding: rawBody, headers: headers ?? defaultHeaders ).responseDecodable(of: T.self) { response in switch response.result { case .success(let model): completion(.success(model)) case .failure(let error): if let statusCode = response.response?.statusCode { print("Raw请求失败,状态码:\(statusCode)") } completion(.failure(error)) } } } }
第四步:定义请求参数与响应模型
现在咱们把原来硬编码的请求参数改成Encodable结构体,响应模型改成Decodable结构体,这样泛型就能自动处理序列化/反序列化:
// 请求参数模型:对应你原来的POST/GET参数 struct PersonRequest: Encodable { let firstName: String let lastName: String let nationalID: String let nationalCode: String let birthDate: Date let image: String let isFemale: Bool let age: Int64 let avg: Double // 如果后端字段是驼峰/下划线,用CodingKeys映射 enum CodingKeys: String, CodingKey { case firstName = "FirstName" case lastName = "LastName" case nationalID = "NationalID" case nationalCode = "NationalCode" case birthDate = "BirthDate" case image = "Image" case isFemale = "isFemale" case age = "Age" case avg = "Avg" } } // 响应模型:根据后端返回的字段定义 struct Person: Decodable { let id: String let firstName: String let lastName: String // 其他字段根据实际需求添加 enum CodingKeys: String, CodingKey { case id = "ID" case firstName = "FirstName" case lastName = "LastName" } }
第五步:使用泛型请求示例
现在你可以轻松调用泛型方法,适配单个模型或数组响应:
// 1. 创建请求参数 let requestParams = PersonRequest( firstName: "Behzad", lastName: "Moulodi", nationalID: "123456", nationalCode: "654321", birthDate: Date(), image: "base64_encoded_image", isFemale: false, age: 30, avg: 92.5 ) // 2. 调用POST请求,获取单个Person响应 let manager = ConnectionManager() manager.post(parameters: requestParams) { (result: Result<Person, Error>) in switch result { case .success(let person): print("请求成功,用户信息:\(person.firstName) \(person.lastName)") case .failure(let error): print("请求失败:\(error.localizedDescription)") } } // 3. 调用GET请求,获取Person列表响应 manager.get(parameters: requestParams) { (result: Result<[Person], Error>) in switch result { case .success(let persons): print("请求成功,用户列表共\(persons.count)条") case .failure(let error): print("请求失败:\(error.localizedDescription)") } }
关键问题解答
- 响应可以是泛型吗? 当然可以!只要你的响应模型遵循
Decodable,不管是单个对象、数组还是嵌套模型,泛型都能自动解析。 - 为什么用Codable替代SwiftyJSON? Codable是Swift原生协议,和Alamofire的
responseDecodable完美适配,代码更简洁,类型更安全,不需要手动解析JSON。 - 如果需要定制序列化逻辑怎么办? 你可以在模型的
encode(to:)或init(from:)方法里自定义逻辑,完全兼容泛型流程。
内容的提问来源于stack exchange,提问作者Behzad Moulodi
相关产品推荐
相关产品推荐

