Skip to content

Add client evolution proposals#149

Open
guoye-zhang wants to merge 1 commit intoapple:mainfrom
guoye-zhang:proposal
Open

Add client evolution proposals#149
guoye-zhang wants to merge 1 commit intoapple:mainfrom
guoye-zhang:proposal

Conversation

@guoye-zhang
Copy link
Copy Markdown
Collaborator

Thank you @nakulbajaj for writing these up

@guoye-zhang guoye-zhang added area/documentation Improvements or additions to documentation. semver/none No version bump required. labels Apr 7, 2026
Copy link
Copy Markdown
Member

@fabianfett fabianfett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First round!

We propose a new HTTP Client API built on two pieces:

1. **Abstract protocol interface** (`HTTPClient`) for dependency injection and testability
2. **Convenience methods** for common use cases with progressive disclosure
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we mention the concrete implementation here again? And say that this gets its own proposal?

Comment on lines +153 to +164
### Middleware

A separate `Middleware` module provides a generic, composable protocol for intercepting and transforming values through a chain. The `Middleware` protocol defines a single `intercept(input:next:)` method that receives a value, processes it, and passes a (potentially transformed) value to the next stage. Middleware pipelines can be built declaratively using the `@MiddlewareBuilder` result builder:

```swift
@MiddlewareBuilder
var pipeline: some Middleware<MyRequest, MyRequest> {
LoggingMiddleware()
AuthenticationMiddleware()
RetryMiddleware()
}
```
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this into the Future directions - Middleware section, as it isn't part of this proposal.


var defaultRequestOptions: RequestOptions { get }

mutating func perform<Return: ~Copyable>(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need an explanation why this is mutable.

The `HTTPClient` protocol provides a single `perform` method that handles all HTTP interactions. The request and response metadata are expressed as `HTTPRequest` and `HTTPResponse` types, from the Swift HTTP types package. The protocol requires `Sendable`, ensuring all conforming clients are safe to share across concurrency domains.

```swift
public protocol HTTPClient<RequestOptions>: Sendable, ~Copyable, ~Escapable {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this potentially ~Copyable, ~Escapable

```swift
public protocol HTTPClient<RequestOptions>: Sendable, ~Copyable, ~Escapable {
associatedtype RequestOptions: HTTPClientCapability.RequestOptions
associatedtype RequestWriter: AsyncWriter, ~Copyable, SendableMetatype
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is an AsyncWriter?

request: HTTPRequest,
body: consuming HTTPClientRequestBody<RequestWriter>?,
options: RequestOptions,
responseHandler: (HTTPResponse, consuming ResponseConcludingReader) async throws -> Return
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we supply the ResponseConcludingReader in a closure?

Request bodies are supported via an `HTTPClientRequestBody`, which encapsulates a closure responsible for writing the request body, in a way that is either `seekable` or `restartable`. A `restartable` request body supports retries (for redirects and authentication challenges), and a `seekable` request body additionally supports resumable uploads. Trailer fields can also be returned from the closure.

```swift
public struct HTTPClientRequestBody<Writer: AsyncWriter & ~Copyable>: Sendable
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this type is dependent on the client that will execute it, how can a user define a standalone request body?

}
```

### Convenience methods for progressive disclosure
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should put all the convenience methods into its own proposal, and refer to them from this proposal with a Future directions.

I think this proposal should just focus on the protocol, its associatedTypes and the perform method.


The proposal consists of several interconnected modules, and the abstract API is defined as part of the `HTTPAPIs` module:
- **HTTPAPIs**: Protocol definitions for `HTTPClient` and shared types
- **NetworkTypes**: Currency types defined as needed for request option capabilities
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow. Do we introduce a new Module here? What is this about? Where will it live?

@@ -0,0 +1,326 @@
# Concrete HTTP Client Implementations
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we put this into a separate pr please?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/documentation Improvements or additions to documentation. semver/none No version bump required.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants