SwiftRestRequests is a lightweight, async/await-first HTTP client written in pure Swift. It helps you build strongly typed REST clients with minimal boilerplate by leaning on Swift's Codable system for both request and response models.
The library is built on top of Foundation's URLSession, so it stays 100% compliant with App Transport Security (ATS) while running everywhere Swift does.
- Async/await API for
GET,POST,PUT,PATCH, andDELETE - Automatic encoding/decoding via
EncodableandDecodable - Response validation and consistent error handling with
RestError - Request interceptors, custom headers, and configurable timeouts
- Built-in support for Basic and Bearer auth as well as TLS/public-key pinning
- Native logging hooks through
swift-log, includingOSLogHandleron Apple platforms
SwiftRestRequests 1.6.3+ supports the following minimum environments:
- iOS 15.0+
- iPadOS 15.0+
- tvOS 15.0+
- watchOS 8.0+
- macOS 12.0+
- Linux (Swift 5.7 or newer)
Toolchain: Swift 5.9 (Xcode 15) or newer.
SwiftRestRequests is distributed as a Swift Package. You can add it via Xcode or directly in your Package.swift.
- Open your project or workspace.
- Choose
File > Add Packages… - Enter
https://github.com/tkausch/SwiftRestRequests - Pick the latest release (recommended) and finish the wizard.
dependencies: [
.package(url: "https://github.com/tkausch/SwiftRestRequests", from: "1.6.3")
]A common pattern is to subclass RestApiCaller and expose one method per REST endpoint. Each method can return a tuple containing the decoded response and the HTTP status, or just an Int status for endpoints without a body.
final class ClientApi: RestApiCaller {
func myGetMethod() async throws -> (HttpBinResponse?, Int) {
try await get(HttpBinResponse.self, at: "get")
}
func myStatusGetMethod() async throws -> Int {
try await get(at: "status/204")
}
}
let baseURL = URL(string: "https://httpbin.org")!
let client = ClientApi(baseUrl: baseURL)
Task {
do {
let (response, status) = try await client.myGetMethod()
print("Status:", status)
print("URL:", response?.url ?? "n/a")
print("Origin:", response?.origin ?? "n/a")
print("Accept header:", response?.headers.accept ?? "n/a")
} catch RestError.failedRestCall(let httpResponse, let status, let error) {
print("Request failed with status:", status)
print("Response:", httpResponse)
print("Error:", String(describing: error))
}
}The request and response types you pass into RestApiCaller must conform to Encodable and Decodable (or Codable when you need both directions). Using shared model objects keeps your API definitions concise and strongly typed.
You can also create an instance of RestApiCaller and call endpoints without subclassing.
let baseURL = URL(string: "https://httpbin.org")!
let apiCaller = RestApiCaller(baseUrl: baseURL)struct HttpBinHeaders: Decodable {
let accept: String
enum CodingKeys: String, CodingKey {
case accept = "Accept"
}
}
struct HttpBinResponse: Decodable {
let url: String
let origin: String
let headers: HttpBinHeaders
}
let (response, status) = try await apiCaller.get(HttpBinResponse.self, at: "get")
print("Status:", status)
print("URL:", response?.url ?? "n/a")struct HttpBinRequest: Encodable {
let key1: String
let key2: Int
let key3: Float
let key4: Bool
let key5: [Int]
}
struct HttpBinResponse: Decodable {
let json: HttpBinRequest
}
let request = HttpBinRequest(key1: "Hello", key2: 1, key3: 2.0, key4: true, key5: [1, 2, 3, 4, 5])
let (response, status) = try await apiCaller.post(request, at: "post", responseType: HttpBinResponse.self)
print("Status:", status)
print("JSON payload:", response?.json ?? request)Apple added native support for SSL public key pinning in iOS 14 (and the corresponding platform releases). The recommended reading is Apple’s article Identity Pinning: How to configure server certificates for your app.
Key takeaways:
- Declare trusted certificates in
Info.plistso ATS knows what to expect per domain. - A pinned CA key must appear in either an intermediate or a root certificate.
- Pins are domain-bound; connections are rejected when the requirement is not met.
- Multiple public keys can be associated with a single domain.
This native pinning works seamlessly with URLSession, and therefore with SwiftRestRequests. If you prefer to manage pinning in code, use the built-in CertificateCAPinning and PublicKeyServerPinning delegates alongside HttpSession.
SwiftRestRequests integrates with the swift-log ecosystem. Plug in your preferred logging backend or use the provided OSLogHandler for unified logging across Apple platforms.
Implement custom interceptors to mutate requests or responses, e.g. to inject headers, refresh tokens, or track analytics. Interceptors run before the request is sent and after the response is received, giving you full control over the network pipeline.
LogNetworkInterceptor(Sources/SwiftRestRequests/interceptors/LogNetworkInterceptor.swift) records requests/responses and can stream verbose network traces in debug builds.AuthorizerInterceptor(Sources/SwiftRestRequests/security/AuthorizerInterceptor.swift) bridges aURLRequestAuthorizerto automatically attach auth headers.- Register interceptors with
registerRequestInterceptor(_:)on theRestApiCallerinstance; they fire in the order they are added and share the sameURLSession. - For TLS pinning helpers see
CertificateCAPinningandPublicKeyServerPinninginSources/SwiftRestRequests/security.
Comprehensive API documentation is available online:
The documentation includes:
- Complete API reference with inline examples
- Getting started guides
- Advanced configuration options
- Security best practices
- Error handling patterns
To build the documentation locally, run:
./scripts/build-docc.shSee DOCC_PUBLISHING.md for more details on building and publishing documentation.
Check the documentation or open a GitHub discussion with questions and ideas. Contributions are welcome—feel free to submit issues or pull requests!
We welcome contributions of all sizes. Read CONTRIBUTING.md for setup instructions, coding standards, and release guidelines before opening a pull request.
