Library / SDK
apple/swift-http-types avatar
apple/swift-http-types

swift-http-types: one HTTP vocabulary for Swift clients and servers

Version-independent HTTP currency types for Swift

1,033 stars80 forksSwiftApache-2.0

At a glance

What is it?
swift-http-types is Apple's Swift package of version-independent HTTP currency types, giving HTTPRequest, HTTPResponse and HTTPFields a shared representation across clients and servers, with Foundation and SwiftNIO bridges around it. The trade-off is that it is deliberately narrow: it models requests, responses and headers, nothing else.
Who is it for?
Adopt swift-http-types when you want one HTTP representation shared by your client code and your server code, and when you want HTTP/2 and HTTP/3 error codes in the same type system. Do not adopt it for a single-URLSession script, for a project already standardised on a different HTTP type library, or expecting a full networking stack, since the package stops at requests, responses and fields.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 28 days ago.
What is it written in?
Mainly Swift, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Currency types, so the client and the server stop disagreeing

The problem this package addresses is narrow and real. On a Swift codebase that both talks HTTP and serves HTTP, the two sides tend to grow different representations of the same thing: one built on URLRequest and URLResponse, the other on NIO's version-specific channel types. Passing data between them means conversion code, and conversion code is where header handling quietly goes wrong.

swift-http-types offers a common set of representations for HTTP requests and responses, and the README describes them as version-independent HTTP currency types designed for both clients and servers, focused on modern HTTP features. Currency is the right word: HTTPRequest, HTTPResponse and HTTPFields are the units of exchange, and they are meant to be held and passed around rather than wrapped.

The scope is deliberately restricted. The HTTPTypes library exposes HTTPRequest, HTTPResponse and HTTPFields, plus the HTTP/2 and HTTP/3 error code types HTTP2ErrorCode and HTTP3ErrorCode. There is no client, no server, no connection management and no TLS. The package describes what the wire format is, and leaves transport to others.

Adding the dependency and the three layers around it

Installation is a SwiftPM dependency clause. The README gives it as the package url with a version range starting at 1.0.0:

swift
dependencies: [
    .package(url: "https://github.com/apple/swift-http-types.git", from: "1.0.0")
]

Around that dependency sit three layers, and knowing which one you need is the first decision. HTTPTypes is the core, holding the request, response and field types plus the error code types. HTTPTypesFoundation adds conveniences for using the new types with Foundation, including bidirectional convertors between the new types and Foundation URL types, and URLSession convenience methods that accept the new types.

The third layer is not in this repository. The NIOHTTPTypes, NIOHTTPTypesHTTP1 and NIOHTTPTypesHTTP2 libraries provide channel handlers that translate the version-specific NIO HTTP types into the new HTTP types, and the README points at swift-nio-extras for them. That detail matters for dependency planning, since a SwiftNIO server needs a second package to reach the shared types, and a project that only wants the core types can ignore both bridges.

Building a request and a response from their parts

The types are constructed from named components rather than a URL string. A request takes a method, scheme, authority and path:

swift
let request = HTTPRequest(method: .get, scheme: "https", authority: "www.example.com", path: "/")

A response is smaller, since most of the interesting content of a response is in its fields:

swift
let response = HTTPResponse(status: .ok)

That shape is the reason to adopt the package. Method, scheme, authority and path are separate, mutable properties, so a request can be built up and then adjusted, the way the Foundation example in the README shows, starting from a URL and then changing the method and the path:

swift
var request = HTTPRequest(method: .get, url: URL(string: "https://www.example.com/")!)
request.method = .post
request.path = "/upload"

Note that the type is a value with mutable members, so the var is doing real work. A method such as .get or .post is a case of an HTTP method type rather than a string, which means the compiler can catch a typo that a raw string would accept silently.

Header fields, including the multi-value case

Header handling is where most HTTP libraries get fiddly, and HTTPFields is designed around typed names. You extend HTTPField.Name to declare a field your application owns, which gives you a checked key instead of a string:

swift
extension HTTPField.Name {
    static let myCustomHeader = Self("My-Custom-Header")!
}

Once the name exists, fields are set and read through subscripting, and a field can carry several values:

swift
// Set
request.headerFields[.userAgent] = "MyApp/1.0"
request.headerFields[.myCustomHeader] = "custom-value"
request.headerFields[values: .acceptLanguage] = ["en-US", "zh-Hans-CN"]

// Get
request.headerFields[.userAgent] // "MyApp/1.0"
request.headerFields[.myCustomHeader] // "custom-value"
request.headerFields[.acceptLanguage] // "en-US, zh-Hans-CN"
request.headerFields[values: .acceptLanguage] // ["en-US", "zh-Hans-CN"]

The single-value subscript returns one string, so reading a repeated field with it collapses the values, while the values subscript returns the array. That distinction is explicit in the API rather than left to convention, and it is the sort of small thing that costs an afternoon when a Content-Language or Accept-Charset header arrives twice.

The custom name is created with a force unwrap, which tells you something about the type's design: the initializer is failable, and a malformed field name is a programming error rather than a runtime condition to handle.

URLSession through the Foundation bridge

HTTPTypesFoundation exists so that a codebase can adopt the new types without abandoning URLSession. The README shows a request built with the new type and then handed to the shared session for an upload, with the response returned as an HTTPResponse so the status can be checked directly:

swift
var request = HTTPRequest(method: .post, url: URL(string: "https://www.example.com/upload")!)
request.headerFields[.userAgent] = "MyApp/1.0"
let (responseBody, response) = try await URLSession.shared.upload(for: request, from: requestBody)
guard response.status == .created else {
    // Handle error
}

The value of the bridge is that the request object you built, with its typed fields, is the same object that goes on the wire, and the response that comes back is the same type the server code uses. If you skip the bridge and use URLRequest directly, you are back to two representations.

The limitation is the one inherent to the design. The bridge converts, it does not extend, so anything URLSession offers that has no counterpart in HTTPFields stays outside this model, and a team that leans on URLSession's own configuration and caching types will still be mixing the two worlds at the edges.

SwiftNIO channel handlers for HTTP/1 and HTTP/2

On the server side, the bridging work is done by channel handlers in swift-nio-extras. The README's example configures an HTTP/2 server pipeline and then works with a duplex handler whose inbound type is HTTPTypeServerRequestPart and outbound type is HTTPTypeServerResponsePart:

swift
channel.configureHTTP2Pipeline(mode: .server) { channel in
    channel.pipeline.addHandlers([
        HTTP2FramePayloadToHTTPServerCodec(),
        ExampleChannelHandler()
    ])
}.map { _ in () }

The handler itself declares which part types it consumes and produces:

swift
final class ExampleChannelHandler: ChannelDuplexHandler {
    typealias InboundIn = HTTPTypeServerRequestPart
    typealias OutboundOut = HTTPTypeServerResponsePart

The request arrives in three parts, head, body and end, and the response is written back the same way, with a head carrying the status and an end carrying the trailers. This is a stream-shaped model, which fits how HTTP actually arrives over a connection.

Two constraints follow. The handlers live in another package, so reaching them means adding swift-nio-extras as a second dependency. Second, the HTTP/1 and HTTP/2 handlers are separate modules, so a server that must speak both pulls in both.

Where the package is the wrong choice

The clearest wrong-tool case is a script that makes one request. If all you need is to fetch a URL and read the body, adding a package plus its Foundation bridge to construct a request is more machinery than the job needs, and URLRequest and URLSession come with Foundation.

The second case is an existing codebase that has already settled on another representation of HTTP types. Migrating means rewriting every request and response construction site, not just adding a dependency, and the payoff only appears if both sides of the wire actually converge on the shared types. A client-only library with no server component gains little from the change.

The third is a requirement for more than the wire format. This package does not do TLS, connection pooling, redirects, retries or caching, so a project looking for a networking layer with those features needs something else, and will then have to decide whether its transport returns HTTPTypes or its own types. The HTTP2ErrorCode and HTTP3ErrorCode types cover the protocol error taxonomy and nothing beyond it.

Apache-2.0, tagged releases, and a monorepo layout

The package is licensed under Apache-2.0, with LICENSE.txt and NOTICE.txt both at the top level, and the NOTICE file is the one to read as well as the licence because it records third-party material included in a distribution. This is not legal advice, but the pairing of the two files in the repository root is a good sign about how the project is packaged for redistribution.

On maintenance, the repository is not archived and the last push was on 2026-09-25. Tagged releases are visible and frequent: 1.8.0 on 2026-09-07, 1.7.0 on 2026-09-03 and 1.6.0 on 2026-06-05. That cadence means you can pin an exact minor version, which is what you want for a type library where a change in shape would ripple through your call sites, and the from clause in the dependency declaration lets you let the range float when you are comfortable with that.

The repository layout shows the working arrangements behind the package. Sources/ and Tests/ hold the code, Benchmarks/ is a separate target, scripts/ and a .swift-format configuration handle tooling, CONTRIBUTING.md describes the contribution process, and .spi.yml points at the Swift Package Index. The README notes that development is otherwise a standard SwiftPM workflow, and that all work is covered by the project's Code of Conduct.

Editorial conclusion

Adopt swift-http-types when you want one HTTP representation shared by your client code and your server code, and when you want HTTP/2 and HTTP/3 error codes in the same type system. Do not adopt it for a single-URLSession script, for a project already standardised on a different HTTP type library, or expecting a full networking stack, since the package stops at requests, responses and fields. Before you commit, check the 1.8.0 tag for the API you rely on, read NOTICE.txt alongside LICENSE.txt under Apache-2.0, and confirm that swift-nio-extras still carries the NIOHTTPTypes modules you need.

Frequently asked questions

How do I add swift-http-types to a Swift project?

Add a dependency clause to Package.swift pointing at the repository url with a version range starting at 1.0.0. The README shows exactly that clause for SwiftPM.

What types does swift-http-types provide?

The HTTPTypes library exposes HTTPRequest, HTTPResponse and HTTPFields, along with the HTTP/2 and HTTP/3 error code types HTTP2ErrorCode and HTTP3ErrorCode. It is a type layer, not a networking stack.

Where do the SwiftNIO channel handlers live?

The NIOHTTPTypes, NIOHTTPTypesHTTP1 and NIOHTTPTypesHTTP2 libraries are not in this repository. The README states they can be found in swift-nio-extras, where they translate the version-specific NIO HTTP types with the new HTTP types.

How do I set a header that can carry several values in swift-http-types?

Use the values subscript, for example request.headerFields[values: .acceptLanguage] = ["en-US", "zh-Hans-CN"]. The plain subscript returns a single string, so reading a repeated field with it collapses the values.

Can I use swift-http-types with URLSession?

Yes. HTTPTypesFoundation provides bidirectional convertors with Foundation URL types and URLSession convenience methods that accept the new types, and the README shows a URLSession.shared.upload call using an HTTPRequest.

Official sources

  1. apple/swift-http-types on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/apple-swift-http-types.svg)](https://hysenlabs.com/projects/apple-swift-http-types)

Was this useful?