Swift SDK

The official Swift SDK for ModelRelay. Provides idiomatic Swift patterns with async/await support, strong typing, and Codable integration.

Installation

Swift Package Manager

Add ModelRelay to your Package.swift:

.package(url: "https://github.com/modelrelay/modelrelay-swift", from: "0.3.5"),

Or in Xcode: File > Add Package Dependencies, then enter:

https://github.com/modelrelay/modelrelay-swift

Quick Start

import ModelRelay

let client = try ModelRelayClient.fromAPIKey(ProcessInfo.processInfo.environment["MODELRELAY_API_KEY"]!)

let answer = try await client.responses.text(
    model: "claude-sonnet-4-5",
    system: "You are a helpful assistant.",
    user: "What is the capital of France?"
)

print(answer)
// "The capital of France is Paris."

Convenience API

Ask — Get a Quick Answer

import ModelRelay

let client = try ModelRelayClient.fromAPIKey(ProcessInfo.processInfo.environment["MODELRELAY_API_KEY"]!)

let answer = try await client.ask(model: "claude-sonnet-4-5", prompt: "What is 2 + 2?")
print(answer) // "4"

Configuration

From API Key

import ModelRelay

// From API key string
let client = try ModelRelayClient.fromAPIKey("mr_sk_...")

// With custom base URL
let client = try ModelRelayClient.fromAPIKey(
    "mr_sk_...",
    baseURL: URL(string: "https://api.modelrelay.ai/api/v1")!
)

Making Requests

ResponseBuilder

The ResponseBuilder provides a fluent API for constructing requests:

let response = try await client.responses.create(
    client.responses
        .builder()
        .model("claude-sonnet-4-5")
        .system("You are a helpful assistant.")
        .user("What is 2 + 2?")
        .maxOutputTokens(256)
        .temperature(0.7)
)

print(response.text())

Multi-Turn Conversations

Build conversations with multiple messages:

let response = try await client.responses.create(
    client.responses
        .builder()
        .model("claude-sonnet-4-5")
        .system("You are a helpful assistant.")
        .user("My name is Alice.")
        .assistant("Hello Alice! How can I help you today?")
        .user("What's my name?")
)

Customer-Attributed Requests

For metered billing, attribute requests to customers:

let response = try await client.responses.create(
    client.responses
        .builder()
        .model("claude-sonnet-4-5")
        .customerId("customer-123")
        .system("You are helpful.")
        .user("Hello!")
)

Streaming

Stream Events

For real-time response streaming:

let stream = try await client.responses.stream(
    client.responses
        .builder()
        .model("claude-sonnet-4-5")
        .user("Write a haiku about programming.")
)

for try await event in stream {
    if event.type == .messageDelta, let delta = event.textDelta {
        print(delta, terminator: "")
    }
}
print()

Structured Output

Parse to Typed Struct

Use Codable structs to parse responses:

import ModelRelay

struct Review: Decodable {
    let risk: String
}

let schema: JSONValue = .object([
    "type": .string("object"),
    "properties": .object([
        "risk": .object(["type": .string("string")])
    ]),
    "required": .array([.string("risk")])
])

let review: Review = try await client.responses.object(
    model: "claude-sonnet-4-5",
    schema: schema,
    prompt: "Classify the risk as low/medium/high"
)

print(review.risk)

SQL Tool Loop

The SDK includes helpers for SQL query generation with validation:

let handlers = SQLToolLoopHandlers(
    listTables: { [SQLTableInfo(name: "users")] },
    describeTable: { _ in SQLTableDescription(table: "users", columns: []) },
    sampleRows: { args in
        SQLExecuteResult(columns: ["id"], rows: [["id": .number(1)]])
    },
    executeSQL: { args in
        SQLExecuteResult(columns: ["id"], rows: [["id": .number(1)]])
    }
)

let result = try await client.sqlToolLoop(
    model: "claude-sonnet-4-5",
    prompt: "Count users",
    handlers: handlers,
    profileId: "profile_1",
    maxAttempts: 3,
    resultLimit: 100
)

print(result.summary)

SQL Tool Loop (Streaming)

let stream = client.sqlToolLoopStream(
    model: "claude-sonnet-4-5",
    prompt: "List recent users",
    handlers: handlers,
    profileId: "profile_1"
)

for try await event in stream {
    switch event {
    case .summaryDelta(let delta):
        print(delta, terminator: "")
    case .executeSQL(let exec):
        print("Rows:", exec.result.rows.count)
    case .result(let result):
        print("Final SQL:", result.sql)
    default:
        break
    }
}

Customer-Scoped Requests

Create a customer-scoped client for attributed requests:

let customer = try client.forCustomer("customer-123")
let text = try await customer.responses.text(
    model: "claude-sonnet-4-5",
    user: "Say hi"
)
print(text)

Customer Token Provider

For frontend use with minted tokens:

let provider = try CustomerTokenProvider(CustomerTokenProviderConfig(
    secretKey: "mr_sk_...",
    request: CustomerTokenRequest(customerExternalId: "customer-123")
))

let tokenClient = try ModelRelayClient.fromTokenProvider(provider)
let text = try await tokenClient.responses.text(
    model: "claude-sonnet-4-5",
    user: "Hi"
)

Workflows + Runs

let spec: JSONValue = .object([
    "version": .string("v1"),
    "nodes": .array([])
])

let compile = try await client.workflows.compile(spec: spec)
if case .success(_, let planHash) = compile {
    let run = try await client.runs.createFromPlan(planHash: planHash)
    print(run.runId)
}

Platform Support

Platform Minimum Version
macOS 13.0
iOS 16.0
tvOS 16.0
watchOS 9.0

Next Steps