Swift SDK
The Swift SDK has the same surface as the JavaScript SDK; this page shows the Swift calls and what differs.
Install
Needs iOS 18 or macOS 15 and later (tvOS 18, watchOS 11, visionOS 2 and Linux
work too). In Package.swift:
dependencies: [
.package(url: "https://github.com/introspection-org/introspection-swift-sdk", from: "0.8.0"),
],
targets: [
.target(name: "App", dependencies: [
.product(name: "IntrospectionSDK", package: "introspection-swift-sdk"),
]),
]In Xcode, use File > Add Package Dependencies with the repository URL.
| Trait | Adds |
|---|---|
| Default | IntrospectionSDK: sign-in, runtimes and runners, tasks and run streams, files, shares, conversations, events, metrics, automations, connections. No OpenTelemetry dependency. |
Configuration | IntrospectionClient.Configuration(config:), read through swift-configuration from INTROSPECTION_TOKEN, INTROSPECTION_BASE_API_URL and INTROSPECTION_DATAPLANE_URL. |
Telemetry | The IntrospectionTelemetry product: track, feedback, identify and gen_ai traces over OTLP. |
Other credentials: Authentication.
Run a task
let client = IntrospectionClient(
controlPlaneURL: URL(string: "https://api.introspection.dev")!,
credentials: BearerToken(apiKey)
)
let runner = try await client.runtimes("support-agent").run(identity: RunnerIdentity(userId: "user_123"))
let answer = try await runner.tasks.start(prompt: "Summarize the open tickets.").text()
print(answer)
let handle = try await runner.tasks.start(prompt: "Say hello.")
for try await event in handle.stream() where event.eventType == .textMessageContent {
print(event.delta ?? "", terminator: "")
}run takes identity (userId, anonymousId, conversationId, tags,
metadata), caller, agentName and ttlSeconds, all optional. With no
identity the task belongs to your credential’s own member; see
Run a task for an end user.
runner.tasks.start(prompt:_:) takes a TaskCreate for the full option set
(title, agent, metadata, files, repositories, tags). Follow-ups use
runner.tasks.runs.create(taskId, text:). Close with runner.close().
Continue and control a task
let next = try await runner.tasks.runs.create(handle.run.taskId, text: "Now draft the reply.")
print(try await next.text())
_ = try await runner.tasks.runs.resume(
handle.run.taskId,
TaskRunResume(resume: [
TaskResumeEntry(interruptId: interruptId, status: "resolved", payload: ["approved": true])
]))
_ = try await handle.cancel() // abort the turn, keep the sandbox warm
_ = try await handle.cancel(options: .drain(within: 60))- One reply at a time. Sending while a reply is still being delivered
throws an
IntrospectionErrorof kind.conflict. - Stream recovery.
stream(options:)andtext(options:)takeRunStreamOptions(readiness wait, overall timeout, reconnect budget). The stream reattaches from its last cursor withLast-Event-ID; semantics: JavaScript. - Interrupts. Answer them as in Ask the user.
- Transcripts.
TranscriptAccumulatorfolds live events and stored conversations into the same entries, for a chat screen.
Files and shares
let uploaded = try await runner.files.upload(FileUpload(data: notes, filename: "notes.txt"))
_ = try await runner.tasks.start(
prompt: "Summarize the attached notes.",
TaskCreate(files: [TaskFileRef(id: uploaded.id)])
)files.createText, download, update and delete cover the rest.
runner.shares.create(ShareCreate(resourceType:resourceId:grantedMemberId:grantedTag:visibleFrom:))
shares a resource; rules: Sharing.
Conversations, events, and metrics
for try await conversation in try runner.conversations.list(ConversationListParams(metadata: ["flow": "support"])) {
print(conversation.id)
}
let result = try await runner.metrics.query(
MetricQueryRequest(
view: "conversations",
metrics: [MetricSpec("sum", measure: "total_cost_usd")],
from: Date().addingTimeInterval(-7 * 86_400),
to: Date()
))A Paginator is an AsyncSequence, with firstPage() and collect(limit:).
events.list(EventListParams(eventName:)) returns events whose typed payloads
are on feedback, annotation, observation and the rest.
Sign in your users
An app with no backend signs its users in directly. With Introspection’s
email code, through a native application:
let auth = AuthClient(
configuration: .init(
controlPlaneURL: URL(string: "https://api.introspection.dev")!,
clientID: "intro_app_...",
project: "my-project",
method: .emailCode,
storage: KeychainSessionStorage()
))
try await auth.signInWithOTP(email: email)
_ = try await auth.verifyOTP(email: email, token: code)
let client = try await auth.client()With your own identity provider, through a jwks application:
IntrospectionClient.federated(subjectToken:clientID:project:). Neither token
is bound to a runtime, so each task names its runtimeId. Setup:
Sign-in for your users.
Errors
Calls throw IntrospectionError, whose kind is .authentication,
.runnerExpired, .insufficientScope, .forbidden, .notFound,
.conflict, .validation, .rateLimited, .unavailable, .network,
.decoding, .cancelled, .runFailed or .streamIncomplete, with status,
code, requestId and body.