Swift-openapi-mock — Record/replay middleware that matches on operationID, not URLs

Hi everyone :waving_hand:

I've just released swift-openapi-mock, an open-source ClientMiddleware for apps built with swift-openapi-generator that lets you record real API responses and replay them offline — with zero network needed.


The problem with existing iOS mocking libraries

Every popular mocking library (OHHTTPStubs, Mocker, MockDuck) operates at the URLSession level. They match on full URLs, which means you need a separate fixture file for every path parameter variation:

/events/1 → one file
/events/2 → another file
/events/3 → yet another...

They also have no awareness of OpenAPI operations — they can't take advantage of the structured contract you've already defined.


How swift-openapi-mock works differently

Instead of hooking into URLSession, it plugs into the ClientMiddleware layer — the same layer your app already uses — and uses operationID as the match key.

One file covers all variations of the same operation, regardless of path parameters.

OHHTTPStubs / Mocker swift-openapi-mock
Match key Full URL operationID
Path param variations One file per value One file for all
Integration layer URLSession ClientMiddleware
Record real responses :cross_mark: :white_check_mark:
Works with swift-openapi-generator :cross_mark: :white_check_mark:

The workflow


1. Enable RecordingClientMiddleware → run the app → real responses saved automatically

2. Run the export script → JSON files copied into your project

3. Commit the JSON files

4. Enable MockClientMiddleware → app runs fully offline

The setup takes about five minutes. If no file is found for an operation, the request falls through to the real network automatically — so you can mock only the endpoints you care about.


Quick start

import OpenAPIMock

let mock = MockClientMiddleware(isEnabled: true)
let recorder = RecordingClientMiddleware(isEnabled: false)

let client = Client(
    serverURL: baseURL,
    transport: transport,
    middlewares: [mock, recorder]
)

Works great in XCTest / Swift Testing too — just pass simulatedLatency: .zero so tests don't wait on fake network delays.


Links


Feedback very welcome — especially if you've run into the path-param problem before and found a different approach. Always curious how others are handling this.