SwiftAIRouter is a production-oriented, vendor-neutral iOS Swift Package for routing AI requests across multiple providers through one clean API.
SwiftAIRouter lets you integrate providers like OpenAI, Anthropic, Google, or custom backends without coupling your app architecture to one vendor.
The package provides:
- Unified request/response types
- Strategy-based provider and model routing
- Automatic fallback on recoverable failures
- Capability-aware model matching
- Optional streaming
- Structured JSON-oriented requests
Most AI integrations become provider-specific quickly, making migration, cost optimization, and reliability difficult.
SwiftAIRouter separates:
- Routing and selection logic
- Provider transport/mapping logic
- Public app-facing API
This gives teams flexibility to switch providers, add failover, and evolve model strategy without rewriting app call sites.
Add the dependency in Xcode using the package URL, or in Package.swift:
.package(url: "https://github.com/Ahsan-Pitafi/SwiftAIRouter.git", from: "1.0.0")Then add the product dependency:
.product(name: "SwiftAIRouter", package: "SwiftAIRouter")Add this line to your Podfile:
pod 'SwiftAIRouter', '~> 1.0'Then run:
pod installimport SwiftAIRouter
let openAIConfig = AIProviderConfiguration(
provider: .openAI,
apiKey: ProcessInfo.processInfo.environment["OPENAI_API_KEY"] ?? "",
baseURL: URL(string: "https://api.openai.com")!,
supportedModels: [
AIModelDescriptor(
id: "gpt-4o-mini",
capabilities: [.textGeneration, .streaming, .jsonOutput],
estimatedLatencyMs: 220,
estimatedCostScore: 18
)
]
)
let config = AIRouterConfiguration(
registeredProviders: [openAIConfig],
defaultRoutingStrategy: .balanced,
timeout: 60,
retryPolicy: AIRetryPolicy(maxRetriesPerProvider: 1, baseDelayMs: 150),
isLoggingEnabled: true
)
let router = AIRouter(configuration: config)
let response = try await router.send(
request: AIRequest(
prompt: "Explain Swift concurrency",
requiredCapabilities: [.textGeneration]
)
)
print(response.text)let anthropic = AIProviderConfiguration(
provider: .anthropic,
apiKey: tokenStore.anthropicKey,
baseURL: URL(string: "https://api.anthropic.com")!,
supportedModels: [
AIModelDescriptor(
id: "claude-3-5-sonnet-latest",
capabilities: [.textGeneration, .streaming, .jsonOutput, .reasoning],
estimatedLatencyMs: 280,
estimatedCostScore: 24
)
],
defaultHeaders: ["anthropic-version": "2023-06-01"]
)let request = AIRequest(
prompt: "Design a resilient networking layer",
preferredProvider: .google,
requiredCapabilities: [.textGeneration, .reasoning]
)
let response = try await router.send(request: request)Supported strategies:
.balanced.lowestLatency.lowestCost.highestCapability.providerPriority([AIProviderKind])
let config = AIRouterConfiguration(
registeredProviders: [openAIConfig, anthropicConfig, googleConfig],
defaultRoutingStrategy: .providerPriority([.openAI, .anthropic, .google]),
retryPolicy: AIRetryPolicy(maxRetriesPerProvider: 1, baseDelayMs: 200)
)
let router = AIRouter(configuration: config)
let response = try await router.send(request: AIRequest(prompt: "Summarize this article"))If the first provider fails with retryable/recoverable conditions, SwiftAIRouter attempts compatible fallback providers.
let stream = try await router.stream(
request: AIRequest(
prompt: "Explain networking in iOS",
requiredCapabilities: [.textGeneration, .streaming]
)
)
for try await chunk in stream {
print(chunk.delta)
}- Never hardcode API keys in source control.
- Inject keys from secure runtime storage (Keychain, encrypted config, CI secrets, environment variables in dev).
- Keep key ownership in the app/infrastructure layer; SwiftAIRouter only consumes injected keys.
- Rotate keys and scope permissions per provider.
- Better cost estimation hooks using token accounting profiles
- Circuit-breaker policy and health scoring per provider
- Richer streaming support for tool-calling events
- Additional provider adapters
- Expanded integration and performance test suites