Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HTTPClientKit

A small async/await HTTP client for Swift, built entirely on URLSession and Foundation. No third-party dependencies, works on iOS and macOS.

Licensed under MIT.

It ships two library targets:

  • HTTPClientKit — the HTTPClient protocol, an URLSession-backed implementation (URLSessionHTTPClient), and typed get/post/put/patch/delete helpers that encode/decode JSON for you.
  • HTTPClientKitMock — MockHTTPClient, a test double that records every request it receives and answers with a fixed response, a fixed error, or a per-request closure.

Installation

.package(url: "https://github.com/PedroAugusto01/HTTPClientKit", from: "1.0.0")
.product(name: "HTTPClientKit", package: "HTTPClientKit"),
.product(name: "HTTPClientKitMock", package: "HTTPClientKit"), // test targets only

Requires iOS 15+ / macOS 12+ (uses URLSession's async data(for:)).

How it works

The protocol

public protocol HTTPClient: Sendable {
    func send(_ request: HTTPRequest) async throws -> HTTPResponse
}

HTTPRequest is transport-agnostic: path (resolved against the client's baseURL), method, queryItems, headers, and an optional body: Data. HTTPResponse carries back statusCode, body, and headers. Everything else in the package is built on top of this one method.

Typed JSON requests

get/post/put/patch/delete are default implementations on HTTPClient, so any conforming type gets them for free:

let client = URLSessionHTTPClient(baseURL: URL(string: "https://api.example.com")!)

let user: User = try await client.get("users/42")

let created: Post = try await client.post("posts", body: NewPost(title: "Hello"))

They call send(_:), classify the status code, decode the body with JSONDecoder/encode the request with JSONEncoder, and throw an HTTPClientError on failure:

public enum HTTPClientError: Error, Equatable, Sendable {
    case invalidURL
    case invalidResponse
    case timeout
    case cancelled
    case transport(String)       // any other URLSession failure
    case encodingFailed(String)
    case decodingFailed(String)
    case clientError(statusCode: Int, body: Data?)   // 4xx
    case serverError(statusCode: Int, body: Data?)   // 5xx
}

Need something the JSON helpers don't cover (multipart, a non-2xx-but-not-an-error status, custom decoding)? Call send(_:) directly and inspect the raw HTTPResponse yourself.

Default headers

URLSessionHTTPClient(baseURL:session:defaultHeaders:) takes headers applied to every request (e.g. Authorization, Accept-Language) — per-request headers passed to get/post/etc. are merged on top and win on conflicts.

Testing with HTTPClientKitMock

import HTTPClientKit
import HTTPClientKitMock

func testFetchesUser() async throws {
    let client = MockHTTPClient(statusCode: 200, body: try JSONEncoder().encode(User(id: 42)))
    let repository = UserRepository(client: client)

    let user = try await repository.fetchUser(id: 42)

    XCTAssertEqual(user.id, 42)
    XCTAssertEqual(client.receivedRequests.first?.path, "users/42")
}

For responses that depend on the request (different status per path, sequential calls, etc), use the closure initializer:

let client = MockHTTPClient { request in
    request.path == "users/1"
        ? HTTPResponse(statusCode: 200, body: userJSON)
        : HTTPResponse(statusCode: 404)
}

Or stub a thrown error directly:

let client = MockHTTPClient(error: HTTPClientError.timeout)

UserRepository only needs to depend on the HTTPClient protocol, never on URLSessionHTTPClient or MockHTTPClient directly — that's what makes the swap possible.

Demo

Sources/HTTPClientKitDemo is a small command-line executable that hits the free jsonplaceholder test API to show a real GET and POST round trip:

swift run HTTPClientKitDemo

Tests

swift test

URLSessionHTTPClientTests exercises the real URLSession-backed client against a stubbed URLProtocol, so the suite never touches the network.

About

A small async/await HTTP client for Swift built on URLSession, with typed JSON helpers and a mock module for tests.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages