Archichil/swift-api-client
A modern, type-safe Swift networking library for building robust API clients with automatic JSON decoding and comprehensive error handling.
Features
β¨ Type-Safe Requests - Protocol-based API specifications ensure compile-time safety π Automatic JSON Decoding - Built-in snake_case to camelCase conversion π¨ Comprehensive Error Handling - Detailed error types for better debugging β‘ Async/Await Support - Modern Swift concurrency for clean, readable code π― Flexible Configuration - Customizable URL sessions and JSON decoders π± Multi-Platform - Supports iOS 13+ and macOS 10.15+
Installation
Swift Package Manager
Add this package to your project by adding the following to your Package.swift file:
dependencies: [
.package(url: "https://github.com/Archichil/swift-api-client.git", from: "1.0.0")
]Or add it through Xcode by going to File β Add Package Dependencies and entering the repository URL.
Quick Start
1. Create an API Client
import APIClient
let baseURL = URL(string: "https://api.example.com")!
let client = APIClient(baseURL: baseURL)2. Define API Specifications
// You can use enum for multiple endpoints
struct GetUserSpec: APIClient.APISpecification {
let userId: Int
var endpoint: String { "/users/\(userId)" }
var method: APIClient.HttpMethod { .get }
var queryParameters: [String: String]? { nil }
var headers: [String: String]? {
["Authorization": "Bearer \(authToken)"]
}
var body: Data? { nil }
}3. Make Requests
let getUserSpec = GetUserSpec(userId: 123)
do {
let user: User = try await client.sendRequest(getUserSpec)
print("User: \(user.name)")
} catch NetworkError.requestFailed(let statusCode) {
print("Request failed with status: \(statusCode)")
} catch NetworkError.decodingFailed(let error) {
print("Decoding failed: \(error)")
} catch {
print("Unexpected error: \(error)")
}API Reference
APIClient
The main client class for making network requests:
public struct APIClient {
public init(
baseURL: URL,
urlSession: URLSession = URLSession.shared,
decoder: JSONDecoder = JSONDecoder(),
useSnakeCaseConversion: Bool = true
)
public func sendRequest<T: Decodable>(_ specification: APISpecification) async throws -> T
}APISpecification Protocol
Define your API requests by conforming to this protocol:
protocol APISpecification {
var endpoint: String { get }
var method: HttpMethod { get }
var headers: [String: String]? { get }
var queryParameters: [String: String]? { get }
var body: Data? { get }
}HTTP Methods
Supported HTTP methods:
enum HttpMethod: String, CaseIterable {
case get = "GET"
case post = "POST"
case patch = "PATCH"
case put = "PUT"
case delete = "DELETE"
case head = "HEAD"
case options = "OPTIONS"
}Error Handling
The library provides comprehensive error types:
enum NetworkError: Error, LocalizedError {
case invalidURL
case invalidResponse
case requestFailed(statusCode: Int)
case decodingFailed(DecodingError)
case unknown(Error)
}Examples
GET Request
struct GetUsersSpec: APIClient.APISpecification {
var endpoint: String { "/users" }
var method: APIClient.HttpMethod { .get }
var headers: [String: String]? {
["Accept": "application/json"]
}
var queryParameters: [String: String]? { nil }
var body: Data? { nil }
}
let users: [User] = try await client.sendRequest(GetUsersSpec())POST Request with JSON Body
struct CreateUserSpec: APIClient.APISpecification {
let user: CreateUserRequest
var endpoint: String { "/users" }
var method: APIClient.HttpMethod { .post }
var headers: [String: String]? {
[
"Content-Type": "application/json",
"Authorization": "Bearer \(authToken)"
]
}
var queryParameters: [String: String]? { nil }
var body: Data? {
try? JSONEncoder().encode(user)
}
}
let newUser: User = try await client.sendRequest(CreateUserSpec(user: userRequest))Raw Data Response
For non-JSON responses (like images or files):
let imageData: Data = try await client.sendRequest(GetImageSpec(imageId: "123"))Advanced Usage
Custom URL Session
let customSession = URLSession(configuration: .ephemeral)
let client = APIClient(baseURL: baseURL, urlSession: customSession)Custom JSON Decoder
let customDecoder = JSONDecoder()
customDecoder.dateDecodingStrategy = .iso8601
let client = APIClient(baseURL: baseURL, decoder: customDecoder)Requirements
- iOS 13.0+ / macOS 10.15+
- Xcode 16.0+
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Package Metadata
Repository: Archichil/swift-api-client
Stars: 1
Forks: 0
Open issues: 0
Default branch: main
Primary language: swift
Topics: api-client, http-client, ios, json, macos, networking, rest-api, spm, swift, swift-package-manager
README: README.md