Swift Digest
Blog | Swift.org Blog

Swift OpenAPI Generator の発表

Introducing Swift OpenAPI Generator

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

この記事の要点

何が発表されたのか

OpenAPI は HTTP サービスを記述するための仕様です。OpenAPI ドキュメントは YAML または JSON で書かれ、ツールから読み取って HTTP リクエストの送受信に必要なコードを生成するといった自動化に利用できます。

OpenAPI は、サービスとそのクライアントの間の API の契約(API contract)を伝える信頼できる唯一の情報源(source of truth)として機能します。これにより、正しい呼び出し方を知るために、不正確な手書きのドキュメントを読んだりネットワーク通信を観察したりする必要がなくなります。サービスを初めて利用するときだけでなく、サービスが進化し続けるときにも、時間がかかり間違いやすい作業を避けられます。

たとえば、/greet エンドポイントへの HTTP GET リクエストを受け付け、任意のクエリパラメータ name を取り、200 OK と JSON のボディを返す GreetingService を考えます。このサービスは次のような OpenAPI ドキュメントで記述できます。

openapi: '3.0.3'
info:
  title: GreetingService
  version: 1.0.0
servers:
  - url: "http://localhost:8080"
    description: "Localhost"
paths:
  /greet:
    get:
      operationId: getGreeting
      parameters:
        - name: name
          in: query
          schema:
            type: string
      responses:
        '200':
          description: A success response with a greeting.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Greeting"
components:
  schemas:
    Greeting:
      properties:
        message:
          type: string
      required:
        - message

Swift OpenAPI Generator は、この OpenAPI ドキュメントを入力として受け取り、HTTP 呼び出しを行うクライアントコード、またはその呼び出しを処理するサーバーコードを生成する SwiftPM プラグインです。生成コードがネットワーク処理を肩代わりするため、クライアント側でもサーバー側でも、利用者は中心となるロジックに集中できます。

何に使えるのか

プラグインの導入

プラグインを使うには、次の依存関係を追加します。

  1. ビルド時にコード生成を行うパッケージプラグイン(swift-openapi-generator
  2. 生成コードが利用するプロトコル定義などを含むランタイムライブラリ(swift-openapi-runtime
  3. 選択した HTTP クライアントライブラリやサーバーフレームワークを差し込むための transport 実装

そのうえで、ターゲットにプラグインを有効化し、API を記述した openapi.yaml と、クライアント・サーバーのどちらを生成するかを指定する設定ファイル openapi-generator-config.yaml を追加します。

生成されたクライアントを使う

クライアント(たとえば iOS アプリ)を開発する場合、OpenAPI の操作ごとに 1 つのメソッドを持つプロトコル APIProtocol と、それを実装した構造体 Client が生成されます。型安全なメソッドを呼ぶだけで HTTP 通信が行えます。

import OpenAPIRuntime
import OpenAPIURLSession

// Instantiate your chosen transport library.
let transport: ClientTransport = URLSessionTransport()

// Create a client to connect to a server URL documented in the OpenAPI document.
let client = Client(
    serverURL: try Servers.server1(),
    transport: transport
)

// Make the HTTP call using a type-safe method.
let response = try await client.getGreeting(.init(query: .init(name: "Jane")))

// Switch over the HTTP response status code.
switch response {
case .ok(let okResponse):
    // Switch over the response content type.
    switch okResponse.body {
    case .json(let greeting):
        // Print the greeting message.
        print("👋 \(greeting.message)")
    }
case .undocumented(statusCode: let statusCode, _):
    // Handle HTTP response status codes not documented in the OpenAPI document.
    print("🥺 undocumented response: \(statusCode)")
}

レスポンスはステータスコードやコンテンツタイプごとに enum のケースとして表現されるため、ドキュメント化された応答も、ドキュメントにない応答(undocumented)も、switch で網羅的に扱えます。

生成されたサーバースタブを使う

サーバーを開発する場合は、操作ごとのメソッドを持つ APIProtocol と、そのハンドラを transport に登録する registerHandlers メソッドが生成されます。APIProtocol に適合する型でハンドラを実装し、transport に登録してサーバーを起動します。

import OpenAPIRuntime
import OpenAPIVapor
import Vapor

// A server implementation of the GreetingService API.
struct Handler: APIProtocol {

    func getGreeting(
        _ input: Operations.getGreeting.Input
    ) async throws -> Operations.getGreeting.Output {
        let message = "Hello, \(input.query.name ?? "Stranger")!"
        let greeting = Components.Schemas.Greeting(message: message)
        return .ok(.init(body: .json(greeting)))
    }
}

// Create the Vapor app.
let app = Vapor.Application()

// Create the transport.
let transport: ServerTransport = VaporTransport(routesBuilder: app)

// Create the request handler, which contains your server logic.
let handler = Handler()

// Register the generated routes on the transport.
try handler.registerHandlers(on: transport)

// Start the server.
try app.run()

transport 実装

Swift OpenAPI Generator は、HTTP ライブラリのインターフェースを ClientTransportServerTransport というプロトコルで抽象化することで、任意の HTTP クライアント・サーバーライブラリと連携できます。swift-log と同様の API package 方式を採り、拡張性を高めています。発表時点で次のような transport 実装が利用できます。

これらに当てはまらない場合は、自分で transport を実装することもできます。

導入・今後の位置づけ

このプロジェクトは、コミュニティのフィードバックを取り入れ、安定版 1.0 に到達するために、開発の早い段階でオープンソース化されました。初期は OpenAPI 仕様の バージョン 3.0.3 の機能の実装に重点が置かれ、OpenAPI 3.1 のサポートに向けた作業も進められています。よく使われる機能の大半をすでにサポートしていますが、未実装の機能も残っており、進捗は GitHub の issue で公開管理されています。これらは発表時点の方針であり、実現を約束するものではありません。

リポジトリの確認や issue・プルリクエストの作成、Swift フォーラム でのフィードバックを通じた参加が呼びかけられています。

関連リンク