A modern Swift web API client built using async/await.
let client = APIClient(baseURL: URL(string: "https://api.github.com"))
// Using the client directly
let user: User = try await client.send(.get("/user")).value
try await client.send(.post("/user/emails", body: ["kean@example.com"]))
// Using an API definition generated with CreateAPI
let repos = try await client.send(Paths.users("kean").repos.get)
Learn about the design of Get and how it leverages async/await in Web API Client in Swift.
You start by instantiating a client:
let client = APIClient(baseURL: URL(string: "https://api.github.com"))
You can customize the client using APIClient.Configuration
(see it for a complete list of available options). You can also use a convenience initializer to configure it inline:
let client = APIClient(baseURL: URL(string: "https://api.github.com")) {
$0.sessionConfiguration.httpAdditionalHeaders = ["UserAgent": "bonjour"]
$0.delegate = YourDelegate()
}
A request is represented using a simple Request<Response>
struct. To create a request, use one of the factory methods:
Request<User>.get("/user")
Request<Void>.post("/repos", body: Repo(name: "CreateAPI"))
Request<Repo>.patch(
"/repos/octokit",
query: [("password", "123456")],
body: Repo(access: .public),
headers: ["Version": "v2"]
)
To send a request, use a client instantiated earlier:
let user: User = try await client.send(.get("/user")).value
try await client.send(.post("/repos", body: Repo(name: "CreateAPI"))
The send
method returns not just the response value, but all of the metadata associated with the request:
public struct Response<T> {
public var value: T
public var data: Data
public var request: URLRequest
public var response: URLResponse
public var statusCode: Int?
public var metrics: URLSessionTaskMetrics?
}
The response can be any Decodable
type. The response can also be optional. And if the response type is Data
, the client simply returns raw response data. If it's a String
, it returns the response as plain text.
If you just want to retrieve the response data, you can also call
data(for:)
.
One of the ways you can customize the client is by providing a custom delegate implementing APIClientDelegate
protocol. For example, you can use it to implement an authorization flow.
final class AuthorizingDelegate: APIClientDelegate {
func client(_ client: APIClient, willSendRequest request: inout URLRequest) async throws {
request.allHTTPHeaderFields = ["Authorization": "Bearer: \(token)"]
}
func shouldClientRetry(_ client: APIClient, withError error: Error) async throws -> Bool {
if case .unacceptableStatusCode(let statusCode) = (error as? APIError), statusCode == 401 {
return await refreshAccessToken()
}
return false
}
}
APIClient
provides elegant high-level APIs, but also gives you complete access to the underlying URLSession
APIs. You can, as shown earlier, change the session configuration, but it doesn't stop there. You can also provide a custom URLSessionDelegate
and implement only the methods you are interested in – APIClient
will handle the rest.
let client = APIClient(baseURL: URL(string: "https://api.github.com")) {
$0.sessionConfiguration.httpAdditionalHeaders = ["UserAgent": "bonjour"]
$0.sessionDelegate = YourSessionDelegate()
}
final class YourSessionDelegate: URLSessionTaskDelegate {
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) {
let protectionSpace = challenge.protectionSpace
if protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
return (.useCredential, URLCredential(...))
} else {
return (.performDefaultHandling, nil)
}
}
}
You can easily add logging to your API client using Pulse. It's a one-line setup.
let client = APIClient(baseURL: URL(string: "https://api.github.com")) {
$0.sessionDelegate = PulseCore.URLSessionProxyDelegate()
// If you also have a session delegate, add it to the delegate chain
$0.sessionDelegate = PulseCore.URLSessionProxyDelegate(delegate: yourDelegate)
}
With Pulse, you can inspect logs directly on your device – and it supports all Apple platforms. And you can share the logs at any time and view them on a big screen using Pulse Pro.
With CreateAPI, you can take your backend OpenAPI spec, and generate all of the response entities and even requests for Get APIClient
.
generate api.github.yaml --output ./OctoKit --module "OctoKit"
Check out OctoKit, which is a GitHub API client generated using CreateAPI that uses Get for networking.
Nuke | Swift | Xcode | Platforms |
---|---|---|---|
Get 0.6 | Swift 5.5 | Xcode 13.2 | iOS 13.0 / watchOS 6.0 / macOS 10.15 / tvOS 13.0 / Linux |
Get 0.1 | Swift 5.5 | Xcode 13.2 | iOS 13.0 / watchOS 6.0 / macOS 10.15 / tvOS 13.0 |
Get is available under the MIT license. See the LICENSE file for more info.