EZNetworking

EZNetworking 是一个 Swift 包,旨在简化您的 iOS 和 macOS 应用程序中的网络请求和 API 交互。 它提供了一个干净且易于使用的 URLSession 抽象层,用于处理 HTTP 请求、响应解码和错误处理。

特性

安装

Swift Package Manager

要使用 Swift Package Manager 将 EZNetworking 集成到您的项目中,请在您的 Package.swift 文件中添加以下依赖项

dependencies: [
    .package(url: "https://github.com/yourusername/EZNetworking.git", from: "1.5.1")
]

用法

创建客户端

Client actor(客户端 Actor)是发出网络请求的主要组件。 您可以使用您自己的 HTTPDownloaderJSONDecoder 自定义它,如果需要的话。

import EZNetworking

let client = Client()

或使用自定义配置

let customDecoder = JSONDecoder()
customDecoder.dateDecodingStrategy = .iso8601

let client = Client(decoder: customDecoder)

发送请求

使用 GenericAPIRequest 创建请求

let url = "https://randomuser.me"
let path = "/api/"
let request = GenericAPIRequest<User>(baseURL: url, path: path)

获取数据

使用 Client 从 API 获取数据

Task {
    do {
        let user = try await client.fetchData(from: request)
        print(user.results.first?.name.first ?? "No name")
    } catch {
        print("Failed to fetch data: \(error.localizedDescription)")
    }
}

使用 API 密钥

如果您的 API 需要 API 密钥,您可以将其包含在请求的 headers(头部)中

let url = "https://api.example.com"
let path = "/data"
let headers = ["Authorization": "Bearer YOUR_API_KEY"]

let request = GenericAPIRequest<MyDataModel>(
    baseURL: url,
    path: path,
    headers: headers
)

使用 Query Items(查询参数)

如果您需要在 API 请求中包含 query parameters(查询参数),您可以使用 queryItems 属性

let url = "https://api.example.com"
let path = "/search"
let queryItems = [
    URLQueryItem(name: "query", value: "Swift"),
    URLQueryItem(name: "limit", value: "10")
]

let request = GenericAPIRequest<MySearchResults>(
    baseURL: url,
    path: path,
    queryItems: queryItems
)

错误处理

EZNetworking 通过 APIError 枚举提供详细的错误处理

enum APIError: Error, LocalizedError {
    case invalidURL
    case httpStatusCodeFailed(statusCode: Int, description: String)
    case decodingError(underlyingError: Error)
    case networkError
    case unknownError
    
    var errorDescription: String? {
        switch self {
        case .invalidURL:
            return "The URL provided was invalid."
        case .httpStatusCodeFailed(let statusCode, let description):
            return "HTTP request failed with status code \(statusCode): \(description)."
        case .decodingError(let underlyingError):
            return "Failed to decode the response: \(underlyingError)."
        case .networkError:
            return "There was a network error."
        case .unknownError:
            return "An unknown error has occurred."
        }
    }
}

测试

EZNetworking 包含一个简单的测试设置来模拟网络响应

import Testing
@testable import EZNetworking

final class Downloader: HTTPDownloader {
    func httpData(from url: URL) async throws -> Data {
        try await Task.sleep(nanoseconds: UInt64.random(in: 100_000_000...500_000_000))
        return testUser
    }
    
    @Test
    func testClientDoesFetchUserData() async throws {
        let downloader = Downloader()
        let client = Client(downloader: downloader)
        let request = GenericAPIRequest<User>(baseURL: "https://randomuser.me", path: "/api/")
        let user = try await client.fetchData(from: request)
        #expect(user.results.count == 1)
    }
}

Static Badge Static Badge

贡献

欢迎贡献! 请随时提交 pull request(拉取请求)或打开 issue(议题)以帮助改进 EZNetworking。