swift之网络请求工具类(Alamofire封装)async

使用 async/await 封装 Alamofire (JSON 格式返回)

下面是一个基于 Alamofire 的现代化封装,使用 Swift 的 async/await 语法,返回 JSON 格式数据。

1. 基础封装

import Alamofire

enum NetworkError: Error {
    case invalidURL
    case invalidResponse
    case statusCode(Int)
    case decodingError(Error)
    case underlying(Error)
    case noData
}

class NetworkManager {
    static let shared = NetworkManager()
    private let session: Session
    
    private init() {
        let configuration = URLSessionConfiguration.af.default
        configuration.timeoutIntervalForRequest = 30
        configuration.timeoutIntervalForResource = 60
        session = Session(configuration: configuration)
    }
    
    // MARK: - 基础请求方法
    private func request(
        _ url: URLConvertible,
        method: HTTPMethod = .get,
        parameters: Parameters? = nil,
        encoding: ParameterEncoding = JSONEncoding.default,
        headers: HTTPHeaders? = nil
    ) async throws -> Data {
        return try await withCheckedThrowingContinuation { continuation in
            session.request(
                url,
                method: method,
                parameters: parameters,
                encoding: encoding,
                headers: headers
            )
            .validate() // 自动验证状态码 200-299
            .responseData { response in
                switch response.result {
                case .success(let data):
                    continuation.resume(returning: data)
                case .failure(let error):
                    if let afError = error.asAFError {
                        switch afError {
                        case .responseValidationFailed(let reason):
                            if case .unacceptableStatusCode(let code) = reason {
                                continuation.resume(throwing: NetworkError.statusCode(code))
                                return
                            }
                        default: break
                        }
                    }
                    continuation.resume(throwing: NetworkError.underlying(error))
                }
            }
        }
    }
    
    // MARK: - 公开方法
    func requestJSON<T: Decodable>(
        _ url: URLConvertible,
        method: HTTPMethod = .get,
        parameters: Parameters? = nil,
        encoding: ParameterEncoding = JSONEncoding.default,
        headers: HTTPHeaders? = nil,
        decoder: JSONDecoder = JSONDecoder()
    ) async throws -> T {
        let data = try await request(
            url,
            method: method,
            parameters: parameters,
            encoding: encoding,
            headers: headers
        )
        
        do {
            return try decoder.decode(T.self, from: data)
        } catch {
            throw NetworkError.decodingError(error)
        }
    }
    
    func requestPlainJSON(
        _ url: URLConvertible,
        method: HTTPMethod = .get,
        parameters: Parameters? = nil,
        encoding: ParameterEncoding = JSONEncoding.default,
        headers: HTTPHeaders? = nil
    ) async throws -> Any {
        let data = try await request(
            url,
            method: method,
            parameters: parameters,
            encoding: encoding,
            headers: headers
        )
        
        do {
            return try JSONSerialization.jsonObject(with: data)
        } catch {
            throw NetworkError.decodingError(error)
        }
    }
}

2. 高级功能扩展

extension NetworkManager {
    // MARK: - 上传文件
    func uploadFile<T: Decodable>(
        _ data: Data,
        to url: URLConvertible,
        method: HTTPMethod = .post,
        headers: HTTPHeaders? = nil,
        decoder: JSONDecoder = JSONDecoder()
    ) async throws -> T {
        return try await withCheckedThrowingContinuation { continuation in
            session.upload(data, to: url, method: method, headers: headers)
                .validate()
                .responseDecodable(of: T.self, decoder: decoder) { response in
                    switch response.result {
                    case .success(let value):
                        continuation.resume(returning: value)
                    case .failure(let error):
                        continuation.resume(throwing: NetworkError.underlying(error))
                    }
                }
        }
    }
    
    // MARK: - 带进度回调的上传
    func uploadWithProgress<T: Decodable>(
        _ data: Data,
        to url: URLConvertible,
        method: HTTPMethod = .post,
        headers: HTTPHeaders? = nil,
        decoder: JSONDecoder = JSONDecoder(),
        progressHandler: @escaping (Progress) -> Void
    ) async throws -> T {
        return try await withCheckedThrowingContinuation { continuation in
            session.upload(data, to: url, method: method, headers: headers)
                .uploadProgress(closure: progressHandler)
                .validate()
                .responseDecodable(of: T.self, decoder: decoder) { response in
                    switch response.result {
                    case .success(let value):
                        continuation.resume(returning: value)
                    case .failure(let error):
                        continuation.resume(throwing: NetworkError.underlying(error))
                    }
                }
        }
    }
    
    // MARK: - 下载文件
    func downloadFile(
        from url: URLConvertible,
        method: HTTPMethod = .get,
        headers: HTTPHeaders? = nil,
        progressHandler: @escaping (Progress) -> Void
    ) async throws -> URL {
        return try await withCheckedThrowingContinuation { continuation in
            let destination: DownloadRequest.Destination = { _, _ in
                let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
                let fileURL = documentsURL.appendingPathComponent(UUID().uuidString)
                return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
            }
            
            session.download(url, method: method, headers: headers, to: destination)
                .downloadProgress(closure: progressHandler)
                .validate()
                .response { response in
                    switch response.result {
                    case .success(let url):
                        if let url = url {
                            continuation.resume(returning: url)
                        } else {
                            continuation.resume(throwing: NetworkError.noData)
                        }
                    case .failure(let error):
                        continuation.resume(throwing: NetworkError.underlying(error))
                    }
                }
        }
    }
}

3. 使用示例

3.1 基本 GET 请求

struct User: Decodable {
    let id: Int
    let name: String
    let email: String
}

// 在 async 上下文中调用
Task {
    do {
        let users: [User] = try await NetworkManager.shared.requestJSON(
            "https://api.example.com/users",
            method: .get
        )
        print("获取用户成功: \(users)")
    } catch {
        print("获取用户失败: \(error)")
    }
}

3.2 POST 请求带参数

struct LoginResponse: Decodable {
    let token: String
    let expiresIn: Int
}

Task {
    do {
        let response: LoginResponse = try await NetworkManager.shared.requestJSON(
            "https://api.example.com/login",
            method: .post,
            parameters: [
                "username": "user@example.com",
                "password": "123456"
            ]
        )
        print("登录成功,token: \(response.token)")
    } catch {
        print("登录失败: \(error)")
    }
}

3.3 上传文件

struct UploadResponse: Decodable {
    let url: String
    let size: Int
}

Task {
    guard let imageData = UIImage(named: "avatar")?.jpegData(compressionQuality: 0.8) else { return }
    
    do {
        let response: UploadResponse = try await NetworkManager.shared.uploadFile(
            imageData,
            to: "https://api.example.com/upload",
            headers: ["Content-Type": "image/jpeg"]
        )
        print("文件上传成功,URL: \(response.url)")
    } catch {
        print("文件上传失败: \(error)")
    }
}

3.4 带进度回调的上传

Task {
    guard let videoData = try? Data(contentsOf: videoURL) else { return }
    
    do {
        let response: UploadResponse = try await NetworkManager.shared.uploadWithProgress(
            videoData,
            to: "https://api.example.com/upload/video",
            progressHandler: { progress in
                print("上传进度: \(progress.fractionCompleted * 100)%")
            }
        )
        print("视频上传成功,URL: \(response.url)")
    } catch {
        print("视频上传失败: \(error)")
    }
}

4. 错误处理建议

extension NetworkError: LocalizedError {
    var errorDescription: String? {
        switch self {
        case .invalidURL:
            return "无效的URL地址"
        case .invalidResponse:
            return "无效的服务器响应"
        case .statusCode(let code):
            return "服务器错误 (代码: \(code))"
        case .decodingError(let error):
            return "数据解析失败: \(error.localizedDescription)"
        case .underlying(let error):
            return "网络请求错误: \(error.localizedDescription)"
        case .noData:
            return "没有接收到数据"
        }
    }
}

// 使用示例
Task {
    do {
        let data: [User] = try await NetworkManager.shared.requestJSON("https://api.example.com/users")
        // 处理数据...
    } catch let error as NetworkError {
        showAlert(message: error.localizedDescription)
    } catch {
        showAlert(message: error.localizedDescription)
    }
}

5. 高级配置

5.1 自定义 JSONDecoder

let customDecoder: JSONDecoder = {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    decoder.dateDecodingStrategy = .iso8601
    return decoder
}()

Task {
    do {
        let user: User = try await NetworkManager.shared.requestJSON(
            "https://api.example.com/user/1",
            decoder: customDecoder
        )
        print("用户信息: \(user)")
    } catch {
        print("错误: \(error)")
    }
}

5.2 请求拦截器

class AuthInterceptor: RequestInterceptor {
    func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
        var request = urlRequest
        if let token = AuthManager.shared.token {
            request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
        }
        completion(.success(request))
    }
}

// 在 NetworkManager 初始化时配置
private init() {
    let configuration = URLSessionConfiguration.af.default
    configuration.timeoutIntervalForRequest = 30
    session = Session(
        configuration: configuration,
        interceptor: AuthInterceptor()
    )
}
posted @ 2025-07-01 20:35  哇哈爽  阅读(252)  评论(0)    收藏  举报