SwiftyAISwiftyAI

Search documentation

Find a docs page by title or section

3

Streaming Utilities

The streaming utilities help with previews, tests, and UI pacing. They operate on strings, so they can be used independently from provider streams.

UtilityUse
simulateReadableStream(_ text:)Turn one string into delayed chunks
simulateReadableStream(_ chunks:)Emit exact chunks for tests and previews
smoothStreamRe-chunk uneven provider output for calmer UI updates
simulateStreamingMiddlewareGive a non-streaming model a stream-like interface

Simulate A Readable Stream

for try await chunk in simulateReadableStream(
    "Streaming text without calling a provider.",
    chunkSize: 8,
    delay: .milliseconds(60)
) {
    print(chunk, terminator: "")
}

You can also provide exact chunks:

let stream = simulateReadableStream(
    ["Hello", ", ", "world"],
    delay: .milliseconds(100)
)

This is useful for SwiftUI previews and unit tests that should not make network calls.

Smooth A Provider Stream

Provider chunks can arrive unevenly. Convert them to strings, then pass them through smoothStream.

let rawText = AsyncThrowingStream<String, Error> { continuation in
    Task {
        do {
            for try await chunk in streamText(
                model: model,
                prompt: "Write a short onboarding paragraph."
            ) {
                continuation.yield(chunk.text)
            }
            continuation.finish()
        } catch {
            continuation.finish(throwing: error)
        }
    }
}
 
let smooth = smoothStream(
    rawText,
    charactersPerChunk: 10,
    interval: .milliseconds(30)
)
 
for try await chunk in smooth {
    print(chunk, terminator: "")
}

Smoothing changes UI pacing only. It does not reduce provider latency or token cost.

Smoothing changesSmoothing does not change
How often your UI receives textProvider response time
Chunk size and cadenceToken usage
Visual typing feelThe final generated text

Simulate Streaming Middleware

When a model only implements AIModel, middleware can turn the final text into chunks:

let streamingModel = wrapLanguageModel(
    model,
    streamMiddleware: [
        simulateStreamingMiddleware(
            chunkSize: 16,
            delay: .milliseconds(40)
        )
    ]
)

Use this for demos and fallback UI. Real provider streaming is still preferable when available.

Related docs

Read stream text for provider streams and custom providers for middleware wrapping.