NNovaNetworkClientSwift networkingGitHub ↗

Beginner · 10 min

Record traffic, replay it offline

Capture a real exchange once and turn it into a deterministic fixture.

You’ll buildA committed cassette that replays the real payload with the network taken away.
1
Step 1

Wrap the scope in a cassette

The first run performs the real request and writes the file. Every run after that replays it, so the test is offline and deterministic.

Recordtraffic,replayitoffline.swift
import NovaNetworkCassette

try await withCassette(at: fixtureURL, upstream: Transport()) { transport in
    let client = NetworkClient(transport: transport)
    let user: User = try await client.load(
        request: request,
        authScope: nil
    )
    #expect(user.name == "Ada")
}
2
Step 2

Check what was written

Bodies are stored as text, keys are sorted, and there are no timestamps, so the file reads like the payload and a real change shows up as a real diff.

Recordtraffic,replayitoffline.swift
{
  "interactions" : [
    {
      "request" : {
        "headers" : { "Authorization" : "<redacted>" },
        "method" : "GET",
        "url" : "https://api.example.com/users/1"
      },
      "response" : {
        "body" : { "text" : "{\"id\":1,\"name\":\"Ada\"}" },
        "status" : 200
      }
    }
  ],
  "version" : 1
}
3
Step 3

Tighten matching only where it matters

Method and full URL match by default, query order ignored. Headers and bodies stay out unless you ask, because a nonce or trace id would break every replay invisibly.

Recordtraffic,replayitoffline.swift
// A cache-busting parameter you do not want to match on:
CassetteMatchRule.methodAndPath

// A search endpoint that varies by payload:
CassetteMatchRule.includingBody

// Content negotiation that really is part of the identity:
CassetteMatchRule.default.matchingHeaders("Accept-Language")
4
Step 4

Replay a sequence, not a constant

Repeated requests replay as episodes, in recorded order, so a polling or pagination flow stays a flow.

Recordtraffic,replayitoffline.swift
let transport = CassetteTransport(
    mode: .replay,
    cassette: try Cassette.load(from: url),
    repeatPolicy: .repeatLast
)
✓

Tutorial complete

A committed cassette that replays the real payload with the network taken away.