Swift Digest
Blog | Swift.org Blog

Swift HTTP Types の発表

Introducing Swift HTTP Types

このダイジェストはClaude Opus 4.7 / 4.8によって生成されたものです(License)。原文はこちら

この記事の要点

何が発表されたのか

Swift HTTP Types は、Swift で HTTP メッセージを扱うための共通の型(currency types)を提供するパッケージです。

HTTP は、クライアント・サーバー・中継など多くの場面で使われる代表的なネットワーク技術です。Apple プラットフォームでは Foundation の URLSession を通じて、サーバーサイドの Swift では SwiftNIO を通じて HTTP が利用されますが、それぞれが独自に HTTP メッセージの表現を持っていると、フレームワークをまたぐたびに型を変換する手間がかかります。

そこで Swift HTTP Types は、HTTP メッセージの基本要素を表す共通の型を用意します。HTTPRequestHTTPResponse をプロジェクト間で共有することで、クライアントとサーバーの間でより多くのコードを再利用でき、型変換のコストや重複した抽象化をなくせます。これらの型は、あらゆる正当な HTTP メッセージを表現できるよう Swift ファーストで設計され、HTTP/3・HTTP/2 を主眼に置きつつ HTTP/1.1 との互換性も維持しています。

将来的には、SwiftNIO の HTTPRequestHead / HTTPResponseHead や、Foundation の URLRequest / URLResponse が持つ HTTP メッセージの詳細を、これらの型で置き換えることが目標とされています。これは発表時点の方針であり、実現を約束するものではありません。

何に使えるのか

リクエストとレスポンスの作成

HTTP リクエストは、メソッド・スキーム・authority・パスから構成されます。次のように直接組み立てられます。

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

Foundation の URL から作ることもできます。

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

作成後にメソッドやパスなどのプロパティを変更できます。

request.method = .post
request.path = "/upload"

レスポンスも同様に簡潔に作れます。

let response = HTTPResponse(status: .ok)

ヘッダーフィールドの操作

ヘッダーフィールドは headerFields プロパティで読み書きします。よく使うフィールドは組み込みで用意されています。

request.headerFields[.userAgent] = "MyApp/1.0"

独自のヘッダーフィールドは HTTPField.Name をエクステンションで定義すれば、同じ構文で扱えます。

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

request.headerFields[.myCustomHeader] = "custom-value"

複数の値を持つフィールドは raw: 添字で配列として直接設定・取得できます。

request.headerFields[raw: .acceptLanguage] = ["en-US", "zh-Hans-CN"]

request.headerFields[.userAgent]            // "MyApp/1.0"
request.headerFields[.acceptLanguage]       // "en-US, zh-Hans-CN"
request.headerFields[raw: .acceptLanguage]  // ["en-US", "zh-Hans-CN"]

Foundation との統合

URLSession と組み合わせると、HTTPRequest をそのまま使って通信できます。型システムと統合されているため、ヘッダーフィールドの指定では補完が効きます。

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
}

SwiftNIO との統合

SwiftNIO との統合は、パッケージが安定した時点で swift-nio-extras を通じて提供される予定です。チャネルハンドラの前に HTTP2FramePayloadToHTTPServerCodec を挿入することで、新しい HTTP 型を使うように設定できます。

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

    func channelRead(context: ChannelHandlerContext, data: NIOAny) {
        switch unwrapInboundIn(data) {
        case .head(let request):
            // Handle request headers
        case .body(let body):
            // Handle request body
        case .end(let trailers):
            // Handle complete request
            let response = HTTPResponse(status: .ok)
            context.write(wrapOutboundOut(.head(response)), promise: nil)
            context.writeAndFlush(wrapOutboundOut(.end(nil)), promise: nil)
        }
    }
}

ボディの扱い

リクエスト・レスポンスのボディは、発表時点ではこのパッケージの対象外です。当面は従来どおり、Foundation では DataInputStream、SwiftNIO では ByteBuffer を使います。ボディの扱いについては、今後コミュニティと議論しながら検討していくとされています。

導入・今後の位置づけ

今回公開されたバージョンは、コミュニティからのフィードバックや議論を得るための出発点と位置づけられています。swift-http-types パッケージ を試したり、Swift フォーラムの #http タグで議論に参加したり、見つけた問題について issue やプルリクエストを送ったりすることで参加できます。

同じ HTTP 領域では、OpenAPI ドキュメントからクライアント・サーバーのコードを生成する Swift OpenAPI Generator の発表 も近い時期に行われています。

関連リンク