日本語 | English
A SwiftPM Build Tool Plugin that generates Kawarimi (ClientTransport mock), KawarimiHandler (APIProtocol default implementation), and KawarimiSpec from OpenAPI at build time. Types, Client, and Server are generated by the official swift-openapi-generator plugin.
Implicit dependency on OpenAPIGenerator: Use both plugins on the same target. Compatible with swift-openapi-generator 1.0.0 or later. The actual type dependency is only on Types.swift (Operations.* Input/Output) and Server.swift (APIProtocol); KawarimiHandler references these. The Kawarimi mock is used by Client (passed as the transport), so Client.swift is not a dependency of the generated code.
dependencies: [
.package(url: "https://github.com/apple/swift-openapi-runtime", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-openapi-generator", from: "1.0.0"),
.package(url: "https://github.com/novr/Kawarimi.git", from: "0.3.0"),
],
targets: [
.target(
name: "MyAPI",
dependencies: [.product(name: "OpenAPIRuntime", package: "swift-openapi-runtime")],
plugins: [
.plugin(name: "OpenAPIGenerator", package: "swift-openapi-generator"),
.plugin(name: "KawarimiPlugin", package: "Kawarimi"),
]
),
]Put one openapi.yaml in the target’s source directory. The build generates Types.swift, Client.swift, Server.swift (via OpenAPIGenerator) and Kawarimi.swift, KawarimiHandler.swift, KawarimiSpec.swift (via KawarimiPlugin).
To configure Types/Client/Server generation, add openapi-generator-config.yaml in the same directory and use swift-openapi-generator configuration.
let client = Client(serverURL: url, transport: Kawarimi())
let response = try await client.getGreeting(...)Kawarimi generates KawarimiSpec.swift alongside the other files at build time. This file contains the full API spec (endpoints and response bodies) as Swift constants. Together with the server-side KawarimiInterceptorMiddleware and Henge API, you can switch mock responses at runtime without recompiling.
KawarimiSpec is generated into your API target and exposes:
KawarimiSpec.meta // title, version, serverURL
KawarimiSpec.endpoints // all endpoints with their possible responses
KawarimiSpec.responseMap // "METHOD:/path" → [statusCode: (body, contentType)]Henge API is the name for the dynamic-mock management API served at /__kawarimi/* (the path is fixed; the name "Henge" refers to the feature).
When using DemoServer as a mock server, register the admin routes and middleware:
let store = try KawarimiConfigStore(configPath: ProcessInfo.processInfo.environment["KAWARIMI_CONFIG"] ?? "config.json")
registerKawarimiRoutes(app: app, store: store)
app.middleware.use(KawarimiInterceptorMiddleware(store: store))| Endpoint | Description |
|---|---|
POST /__kawarimi/configure |
Enable a mock response for a path/method/statusCode |
GET /__kawarimi/status |
List active overrides |
POST /__kawarimi/reset |
Clear all overrides |
GET /__kawarimi/spec |
Return the full KawarimiSpec (meta + endpoints) |
Example — enable a 200 mock for GET /api/greet:
curl -X POST http://localhost:8080/__kawarimi/configure \
-H "Content-Type: application/json" \
-d '{"path":"/api/greet","method":"GET","statusCode":200,"isEnabled":true}'DynamicMockTransport is a hand-written ClientTransport (generated into DemoAPI) that lets the client switch between the real server and the mock server at runtime:
let transport = DynamicMockTransport(
underlying: URLSessionTransport(),
realBaseURL: URL(string: "https://example.com/api")!,
mockBaseURL: URL(string: "http://localhost:8080/api")!,
useMockServer: true
)
let client = Client(serverURL: transport.mockBaseURL, transport: transport)Set x-kawarimi-mockId header to target a specific named override:
transport.mockId = "error-case"KawarimiConfigStore reads and writes overrides to a JSON file (default: config.json in the working directory). The file format uses KawarimiConfig (overrides array). Run DemoServer with the Example directory as the current working directory so that config.json is read and written there (e.g. cd Example && swift run DemoServer). Set the KAWARIMI_CONFIG environment variable to override the path. Override body or contentType that is empty string is normalized to “not set” when saved; at response time, empty body means fall back to the spec response (no custom body). You can pass pathPrefix (default "/api") to KawarimiConfigStore if your API is mounted at a different path.
cd Example && swift run DemoServer # config.json in Example/
KAWARIMI_CONFIG=/tmp/mock-config.json swift run DemoServerRun swift run DemoAppUI to open a macOS window that shows all endpoints from the running server and lets you switch mock responses via a picker — no terminal required.
cd Example && swift build
swift run DemoServer # in another terminal
swift run DemoApp # client
swift run DemoAppUI # SwiftUI management UI (optional)Ways to guarantee the order of generating Types/Client/Server vs Kawarimi (mock/handler):
- Command 1: Generate Types / Client / Server from openapi.yaml (same as current swift-openapi-generator).
- Command 2: Generate Kawarimi.swift / KawarimiHandler / KawarimiSpec from the same openapi.yaml.
- Include Command 1’s
outputFiles(at least Types.swift and Server.swift, which KawarimiHandler depends on) in Command 2’sinputFiles. SwiftPM’s build graph will run Command 2 after Command 1. - Benefit: Execution order is fixed by input/output within a single plugin. If you later switch to a setup that does not depend on the official generator, you only need to replace Command 1.
- User adds both the official swift-openapi-generator plugin and KawarimiPlugin to the same target (official for Types/Client/Server, Kawarimi for mocks).
- SwiftPM does not guarantee execution order across plugins, so document that the official plugin should be applied first and Kawarimi second (in README or examples).
- Caveat: Order is not guaranteed by the build system; both plugins effectively take openapi.yaml as input unless Kawarimi is changed to take already-generated Swift (e.g. via Swift Syntax) as input.
- Swift 6.2+ / macOS 14+
- Supported: 200 + application/json operations, schemas referencing components/schemas via $ref
- See the repository for more.