はじめに
2023/7/10にSwift Http Typesなるものができました
本記事では公式ドキュメントの要約とその詳細について執筆していきます。
Swift HTTP Typeでできること
さきにまとめを書いておきます。
- Swift HTTP TypesではNIOでのURL SessionやFoundationでのHTTPリクエスト、レスポンスといったことが可能
- 複数フレームワークを使用せずにHTTP通信のやりとりを行えるため、複数フレームワーク間で変換するコストが削減される。
- HTTP/1.1、HTTP/2、HTTP/3に対応
- Foundation、Swift NIOとの連携可能
Swift HTTP Typesとは
Swift HTTP typesはHTTPメッセージの構成要素と共通表現について提供するものである。
HTTP RequestとHTTP Responseはクライアント、サーバの両方のユースケースのHTTPメッセージを表している。
複数のプロジェクトでこれを採用することで、より多くのクライアントとサーバ間で共有できるようになり、複数のフレームワークを使用する場合にタイプ間で変換するコストが削減される。
これらの型はHTTP/3やHTTP/2との互換性はもちろん、HTTP/1.1との互換性も維持している。
このパッケージの目標として、SwiftNIOのHTTP requestやHTTP response、FoundationのURL RequestやURL Responseに代わるものとなることを目指す。
このパッケージではあらゆるHTTP通信に適するように設計されており、既存のフレームワークに関連付けられていないため、重複したHTTP抽象化の必要がなくなる。
使用例
HTTPリクエストの核はメソッド、スキーム、権限、パスで構成されている。
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)
プロパティを使用してヘッダーフィールドにアクセスし、変更もできる
request.headerFields[.userAgent] = "MyApp/1.0"
共通のヘッダーフィールドが組み込まれており、カスタムヘッダーフィールドと値の拡張を簡単にできるため、ビジネスロジックな場面でも容易に使用できる
extension HTTPField.Name {
static let myCustomHeader = Self("My-Custom-Header")!
}
request.headerFields[.myCustomHeader] = "custom-value"
配列に入った値からもヘッダーフィールドを直接設定できる
request.headerFields[raw: .acceptLanguage] = ["en-US", "zh-Hans-CN"]
ヘッダーフィールドへのアクセスもほぼ同じで、システム定義のフィールド、または独自の拡張機能を使用できる
request.headerFields[.userAgent] // "MyApp/1.0"
request.headerFields[.myCustomHeader] // "custom-value"
request.headerFields[.acceptLanguage] // "en-US, zh-Hans-CN"
request.headerFields[raw: .acceptLanguage] // ["en-US", "zh-Hans-CN"]
Foundationとの連携
URL Sessionを使用すると、“www.example.com”にPOSTでリクエストするための新たなHTTP Requestを作成することも容易である。
このリクエストの"User-Agent"ヘッダーフィールドを設定することはとても直感的なものであり、type systemによる自動補完を提供する。
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
}
Swift NIOとの連携
Swift NIOとの統合は、このパッケージが安定バージョンになると、swift-nio-extrasパッケージで利用できるようになる。
新しいHTTPタイプで使用するNIOチャンネルハンドラーを構成するには、他のチャンネルハンドラーの前に"HTTP2FramePayloadToHTTPServerCodec"を追加する。
NIOTSListenerBootstrap(group: NIOTSEventLoopGroup())
.childChannelInitializer { channel in
channel.configureHTTP2Pipeline(mode: .server) { channel in
channel.pipeline.addHandlers([
HTTP2FramePayloadToHTTPServerCodec(),
ExampleChannelHandler()
])
}.map { _ in () }
}
.tlsOptions(tlsOptions)
この例のチャンネル実装では、HTTP requestとHTTP responseの両方のタイプを処理する。
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)
}
}
}
まとめ
- Swift HTTP TypesではNIOでのURL SessionやFoundationでのHTTPリクエスト、レスポンスといったことが可能
- 複数フレームワークを使用せずにHTTP通信のやりとりを行えるため、複数フレームワーク間で変換するコストが削減される。
- HTTP/1.1、HTTP/2、HTTP/3に対応
- Foundation、Swift NIOとの連携可能
これを使うことで、いままでより高速なHTTP通信を実現することが出来るかもですね。
いつかFoundation、Swift NIOとの速度比較なども行ってみようと思います。
では、読んでくれてありがとうございました!