Skip to Content
Platform
SDKsSwift

Swift SDK

Run tasks against a deployed runtime from an iOS or macOS app, or a Swift server, and read what the agent did.

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.

TraitAdds
DefaultIntrospectionSDK: sign-in, runtimes and runners, tasks and run streams, files, shares, conversations, events, metrics, automations, connections. No OpenTelemetry dependency.
ConfigurationIntrospectionClient.Configuration(config:), read through swift-configuration  from INTROSPECTION_TOKEN, INTROSPECTION_BASE_API_URL and INTROSPECTION_DATAPLANE_URL.
TelemetryThe 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 IntrospectionError of kind .conflict.
  • Stream recovery. stream(options:) and text(options:) take RunStreamOptions (readiness wait, overall timeout, reconnect budget). The stream reattaches from its last cursor with Last-Event-ID; semantics: JavaScript.
  • Interrupts. Answer them as in Ask the user.
  • Transcripts. TranscriptAccumulator folds 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.

Next

Last updated on