iOS SDK
Swift · iOS 16+ · Swift Package Manager
The MAIG Swift SDK provides a native async/await interface for generating text and streaming responses from any AI model configured in your project. Requests are authenticated with your project API key and routed through MAIG's gateway — your provider credentials never leave the server.
Requirements
- iOS 16.0 or later
- Swift 5.7 or later (Xcode 14+)
- A MAIG project API key — see the Dashboard Setup guide
Installation
Swift Package Manager
In Xcode, go to File → Add Package Dependencies and enter the repository URL:
https://github.com/your-org/AIGatewaySDK
Select the AIGatewaySDK package and add it to your app target. Then import the module wherever you need it:
import AIGatewaySDK
Alternatively, add it as a dependency in your own Package.swift:
dependencies: [
.package(url: "https://github.com/your-org/AIGatewaySDK", branch: "main")
]
Initialization
Create one shared AIGatewayClient instance — typically at the app level or in a dedicated service class:
import AIGatewaySDK
let client = AIGatewayClient(
apiKey: "maig_YOUR_PROJECT_KEY",
baseURL: URL(string: "https://api.maig.dev")!
)
.xcconfig, or a secrets manager.
Generating text
Use generateText to make a single round-trip request and receive the complete response as a String:
let response = try await client.generateText(
prompt: "Summarize WWDC 2025 in three sentences.",
options: nil
)
print(response)
Named routes
Pass a route name as the model field to target a specific route you configured in the dashboard. This decouples your app from concrete model names — you can change the underlying model in the dashboard without shipping an app update:
// Target the route named "chat" in your project
let options = GenerateOptions(model: "chat")
let response = try await client.generateText(
prompt: "Summarize WWDC 2025 in three sentences.",
options: options
)
You can also pass a literal model name (e.g. "gpt-4o") to bypass route selection entirely.
GenerateOptions
All available options:
let options = GenerateOptions(
model: "summarizer", // route name or literal model name; nil uses project default
userId: currentUser.id, // optional: appears in dashboard logs
maxTokens: 512
)
let response = try await client.generateText(
prompt: "Write a short product description for an AI routing SDK.",
options: options
)
Streaming text
Use streamText to receive tokens as they are generated. The method returns an AsyncSequence of String tokens:
var output = ""
for try await token in client.streamText(prompt: "Tell me a story about a robot.") {
output += token
await MainActor.run { label.text = output }
}
Streaming uses Server-Sent Events (SSE) under the hood, parsed incrementally over a URLSession byte stream. It works with both SwiftUI and UIKit.
Error handling
Both generateText and streamText throw AIGatewayError. Handle the cases your app cares about:
do {
let response = try await client.generateText(prompt: userInput, options: nil)
display(response)
} catch AIGatewayError.authFailure {
// Invalid or expired API key
showAlert("Authentication failed. Check your API key in Settings.")
} catch AIGatewayError.networkError(let underlying) {
// Device is offline or connection was lost mid-stream
showAlert("Network error: \(underlying.localizedDescription)")
} catch AIGatewayError.serverError(let statusCode, let message) {
// Gateway or upstream provider returned a non-2xx response
print("Server error \(statusCode): \(message ?? "unknown")")
} catch {
print("Unexpected error: \(error)")
}
Error cases
authFailure— the project API key is missing, malformed, or revokednetworkError(Error)— transport-level failure; the associated value is the underlyingURLErrorserverError(Int, String?)— the gateway responded with a non-2xx HTTP status; includes the status code and an optional message string
Prompt Management
The SDK includes PromptStore — a lightweight class that syncs server-managed prompts to a local cache on device. Update prompts in the dashboard and your app picks up the change on its next sync() call, no app release required.
let store = PromptStore(apiKey: "maig_YOUR_PROJECT_KEY")
// At app launch — fetches only changed prompts
try await store.sync()
// At inference time — synchronous in-memory read, no network call
let storedMessages = store.getPrompt(named: "support-bot-system") ?? []
let messages = storedMessages + [Message.user("My order hasn't arrived.")]
let result = try await client.generateText(messages: messages)
See the Prompt Management guide for full details on creating prompts in the dashboard, handling the first-launch case, and the complete API reference.
Semantic versioning
Prompts are versioned as major.minor (e.g. v1.2). A minor bump (v1.0 → v1.1) is a safe edit — no new variables. A major bump (v1.x → v2.0) means a new {{VARIABLE}} was added and your app code needs to supply it.
Version pinning
Pin a prompt to a major version so your app only ever receives minor updates within that major. A major bump is never delivered until you explicitly move the pin — protecting you from broken variable substitution at runtime.
Option A — JSON config file (default location)
Add maig-prompts.json to your Xcode project bundle. The SDK looks for this file automatically — no extra code needed:
{
"pinned": {
"support-bot": 1,
"onboarding-flow": 2
}
}
// SDK loads maig-prompts.json from the bundle automatically
let store = PromptStore(apiKey: "maig_YOUR_PROJECT_KEY")
// Custom filename (without .json): override the default
let store = PromptStore(apiKey: "maig_YOUR_PROJECT_KEY", configFile: "my-config")
// Pass nil to disable config file loading entirely
let store = PromptStore(apiKey: "maig_YOUR_PROJECT_KEY", configFile: nil)
Option B — Runtime API
let store = PromptStore(apiKey: "maig_YOUR_PROJECT_KEY")
// Pin before calling sync() — runtime pins override the JSON file
store.pin("support-bot", majorVersion: 1)
store.pin("onboarding-flow", majorVersion: 2)
try await store.sync()
When you are ready to adopt a new major version, update the pin number and handle the new variable in your app code:
// Ready for v2 — bump the pin and supply the new variable
store.pin("support-bot", majorVersion: 2)
try await store.sync()
if let result = store.getPrompt(named: "support-bot", variables: [
"PRODUCT": "Acme Store",
"TIER": userTier, // new variable added in v2
]) {
let messages = result.messages + [Message.user(userInput)]
}
Next steps
- Configure fallback routing in the dashboard to improve resilience when a provider is unavailable
- View per-request logs, token counts, and latency in the dashboard's Logs tab
- Manage prompts server-side with Prompt Management
- Questions? Email support@maig.dev