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

如何为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:06:33