Record your first request
Add the Swift SDK, trace a Foundation Models response, and inspect it in LedgeKit.
This example records one request to Apple's on-device Foundation Models model. You need an eligible device with Apple Intelligence available, an iOS/macOS 26 destination, and access to a LedgeKit workspace.
1. Add the package
In Xcode, choose File → Add Package Dependencies and enter:
https://github.com/noahtovares/ledgekit-iosChoose Commit and use 2102e4dd6951d04c2aca7b364f56d0eaac4576f9, the SDK revision
these examples target. Link the LedgeKit product to your app target. See
installation for Swift Package Manager and repository access.
2. Create an app and test write key
Open the console, choose your workspace, and create an app. Use my-app as its service name for this example. In that app's Test environment, open Keys and create a write key. Copy the token when it is shown; the console does not display the complete token again.
The key chooses the app and environment. serviceName must match that app.
Read keys cannot upload traces. Keep the key out of source control and supply it
through your local development configuration.
3. Record a summary
import Foundation
import FoundationModels
import LedgeKit
enum SummaryError: Error { case modelUnavailable }
@available(iOS 26.0, macOS 26.0, *)
func recordSummary(apiKey: String) async throws -> String {
guard SystemLanguageModel.default.availability == .available else {
throw SummaryError.modelUnavailable
}
let client = LedgeClient(
configuration: .init(serviceName: "my-app"),
transport: LedgeHTTPTransport(apiKey: apiKey)
)
await client.start()
let native = LanguageModelSession(
instructions: "Summarize the supplied text in one sentence."
)
let session = LedgeLanguageModelSession(
native,
recordingTo: client,
traceName: "summarize",
agent: .init(name: "summarizer", version: 1)
)
let text = "A durable outbox saves requests locally before uploading them."
let response = try await session.respond(
to: "Summarize: \(text)",
inputData: .object(["text": .string(text)])
)
// Useful for this one-shot example; normal uploads run asynchronously.
await client.flush()
return response.content
}Call try await recordSummary(apiKey: testWriteKey) from an async task and handle
the returned string or thrown error in your UI. The recordingTo: adapter owns
the run and finalizes it when the request succeeds, fails, or is cancelled.
Your app receives Apple's original response. In an app with repeated requests, keep one client at application scope; see configuration.
4. Find the trace
Return to the same app's Test environment and open Traces. Select the
summarize trace and its summarizer invocation. You should see the model input,
response, agent version, timing, and the original text under Input data.
flush() waits for pending upload attempts; it does not guarantee successful
delivery. If the trace is missing, check the app, environment, write-key access,
network connectivity, and delivery diagnostics.
Continue
- Record structured responses with
Generabletypes. - Group requests into a run when a workflow makes several calls.
- Save inputs to a dataset and evaluate your next implementation.