Skip to content

Commit 4bca01d

Browse files
authored
Merge pull request #7 from BrentMifsud/bm/transport-protocol
Extract Transport protocol and migrate to swift-http-types
2 parents ed7929c + ad3ae2f commit 4bca01d

38 files changed

+1891
-2425
lines changed

CLAUDE.md

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
44

5+
## Git Workflow
6+
7+
- **Never commit directly to `main`.** All changes must be made on a feature branch and merged via pull request.
8+
- Branch naming convention: `bm/<short-description>` (e.g., `bm/transport-protocol`, `bm/fix-cache-bug`)
9+
- If you find yourself on `main`, create a feature branch before making any changes.
10+
511
## Build and Test Commands
612

713
```bash
@@ -20,26 +26,40 @@ swift test --filter "MiddlewareTests/middlewareCallOrder"
2026

2127
## Architecture
2228

23-
Simplicity is a type-safe HTTP client library for Swift, inspired by swift-openapi-generator's client design.
29+
Simplicity is a type-safe HTTP client library for Swift, inspired by swift-openapi-generator's client design. It uses Apple's `swift-http-types` (`HTTPTypes`, `HTTPTypesFoundation`) for standard HTTP primitives.
2430

2531
### Core Components
2632

27-
- **HTTPClient** (`Sources/Simplicity/HTTPClient.swift`): The main entry point. Executes requests through a middleware chain using `URLSession`. The `send(request:)` method builds the middleware chain in reverse order, so middlewares execute in the order they were added.
33+
- **Client protocol** (`Sources/Simplicity/Protocol/Client.swift`): The main entry point. Defines `send(_:)` and `upload(_:)` methods that execute requests through a middleware chain. Uses Apple's `HTTPRequest.Method`, `HTTPResponse.Status`, and `HTTPFields` throughout.
34+
35+
- **Request protocol** (`Sources/Simplicity/Protocol/Request.swift`): Defines type-safe requests with associated `RequestBody`, `SuccessResponseBody`, and `FailureResponseBody` types. Uses Apple's `HTTPRequest.Method` and `HTTPFields` for properties. Provides default JSON encoding/decoding implementations. Use `Never?` for `RequestBody` on requests without a body.
36+
37+
- **Response struct** (`Sources/Simplicity/HTTP/Response.swift`): Wraps Apple's `HTTPResponse` and adds typed decoding via `decodeSuccessBody()` / `decodeFailureBody()`. Convenience accessors: `.status`, `.headerFields`.
38+
39+
- **Middleware protocol** (`Sources/Simplicity/Protocol/Middleware.swift`): Intercepts requests and responses via an `intercept` method that receives a `next` closure. Uses `MiddlewareRequest` and `MiddlewareResponse` structs that embed Apple's `HTTPRequest` and `HTTPResponse`.
40+
41+
- **Transport protocol** (`Sources/Simplicity/Protocol/Transport.swift`): Abstracts the network layer at the `HTTPRequest`/`HTTPResponse` level. `URLSessionTransport` is the default implementation wrapping `URLSession`; tests inject `MockTransport`.
42+
43+
- **URLSessionClient** (`Sources/Simplicity/Implementation/URLSessionClient.swift`): Concrete `Client` implementation. Delegates network calls to a `Transport` (defaults to `URLSessionTransport`). Accepts a `URLSession` convenience init for backward compatibility.
2844

29-
- **HTTPRequest protocol** (`Sources/Simplicity/Protocol/HTTPRequest.swift`): Defines type-safe requests with associated `RequestBody` and `ResponseBody` types. Provides default JSON encoding/decoding implementations. Use `Never?` for `RequestBody` on requests without a body.
45+
### HTTP Types
3046

31-
- **Middleware protocol** (`Sources/Simplicity/Protocol/Middleware.swift`): Intercepts requests and responses via an `intercept` method that receives a `next` closure. Middlewares can modify requests before calling `next` and inspect/modify responses after.
47+
The library uses Apple's `swift-http-types` throughout:
48+
- `HTTPRequest.Method` (extensible struct) instead of custom enum
49+
- `HTTPResponse.Status` (supports any status code, `.kind` categorization) instead of custom enum
50+
- `HTTPFields` (case-insensitive, multi-value, type-safe names) instead of `[String: String]`
3251

3352
### Request Body Handling
3453

35-
The `HTTPRequest` protocol has special handling for bodyless requests:
36-
- Use `Never?` as `RequestBody` and set `httpBody` to `nil` for GET/DELETE requests
37-
- The protocol has private extensions that skip body encoding when `RequestBody` is `Never` or `Never?`
54+
The `Request` protocol has special handling for bodyless requests:
55+
- Use `Never?` as `RequestBody` and set `body` to `nil` for GET/DELETE requests
56+
- The protocol has extensions that skip body encoding when `RequestBody` is `Never` or `Never?`
3857

3958
### Testing Approach
4059

41-
Tests use Swift Testing framework (`@Suite`, `@Test`, `#expect`). Network tests mock `URLSession` via `MockURLProtocol` configured on an ephemeral session configuration.
60+
Tests use Swift Testing framework (`@Suite`, `@Test`, `#expect`). Network tests inject a `MockTransport` conforming to the `Transport` protocol — each test gets its own isolated handler closure with zero shared state.
4261
- When running tests, be sure to run tests for all supported platforms
62+
- Prefer `@Test(arguments:)` for parameterized tests over writing separate test functions for each input variation. One parameterized test with an array of inputs is cleaner than many near-identical test cases.
4363

4464
## Documentation Lookup (Context7)
4565

@@ -48,4 +68,4 @@ When working on this project, **always use Context7** to look up documentation b
4868
Key libraries relevant to Simplicity:
4969
- **Foundation**`URLSession`, `URLRequest`, `URLCache`, `JSONEncoder`/`JSONDecoder` APIs
5070
- **Swift Concurrency** — actors, `async`/`await`, `@Sendable`, `@concurrent`, `nonisolated`
51-
- **Swift Testing**`@Suite`, `@Test`, `#expect`, `#require`, `Issue.record`
71+
- **Swift Testing**`@Suite`, `@Test`, `#expect`, `#require`, `Issue.record`

Package.resolved

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@
33

44
import PackageDescription
55

6+
let swiftSettings: [SwiftSetting] = [
7+
.swiftLanguageMode(.v6),
8+
.strictMemorySafety(),
9+
.defaultIsolation(nil),
10+
.enableUpcomingFeature("ExistentialAny"),
11+
.enableUpcomingFeature("InferIsolatedConformances"),
12+
.enableUpcomingFeature("InternalImportsByDefault"),
13+
.enableUpcomingFeature("MemberImportVisibility"),
14+
.enableUpcomingFeature("ImmutableWeakCaptures"),
15+
.treatAllWarnings(as: .error),
16+
]
17+
618
let package = Package(
719
name: "Simplicity",
820
platforms: [
@@ -19,21 +31,22 @@ let package = Package(
1931
targets: ["Simplicity"]
2032
),
2133
],
34+
dependencies: [
35+
.package(url: "https://github.com/apple/swift-http-types.git", from: "1.0.0"),
36+
],
2237
targets: [
2338
.target(
2439
name: "Simplicity",
25-
swiftSettings: [
26-
.swiftLanguageMode(.v6),
27-
.strictMemorySafety(),
28-
.defaultIsolation(nil),
29-
.enableUpcomingFeature("InferIsolatedConformances"),
30-
.enableUpcomingFeature("NonisolatedNonsendingByDefault"),
31-
.enableUpcomingFeature("InternalImportsByDefault"),
32-
]
40+
dependencies: [
41+
.product(name: "HTTPTypes", package: "swift-http-types"),
42+
.product(name: "HTTPTypesFoundation", package: "swift-http-types"),
43+
],
44+
swiftSettings: swiftSettings
3345
),
3446
.testTarget(
3547
name: "SimplicityTests",
36-
dependencies: ["Simplicity"]
48+
dependencies: ["Simplicity"],
49+
swiftSettings: swiftSettings
3750
),
3851
]
3952
)

0 commit comments

Comments
 (0)