β οΈ UNOFFICIAL LIBRARY: This is an unofficial Swift SDK for the Anthropic Claude API and is not affiliated with, endorsed, or supported by Anthropic in any way. This library is developed and maintained independently.
π€ AI-ASSISTED DEVELOPMENT: This SDK was designed and implemented using Claude Code, Anthropic's agentic coding tool, following strict Test-Driven Development (TDD) and Behavior-Driven Development (BDD) principles.
A native Swift SDK for the Anthropic Claude API, designed for iOS and macOS applications. Built with modern Swift features including async/await, actors, and comprehensive error handling.
- π Native Swift API: Idiomatic Swift patterns with async/await support
- π Real-time Streaming: AsyncSequence-based streaming for live responses
- π Tool Integration: Built-in support for Claude's tool use capabilities
- π§ Extended Thinking: Access to Claude's reasoning process
- π¦ Batch Operations: Efficient bulk message processing
- π File Management: Upload and manage files for Claude processing
- β‘ Performance Optimized: Connection pooling, caching, and retry strategies
- π― Type Safe: Comprehensive type definitions for all API interactions
- π± Mobile Ready: Optimized configurations for iOS/macOS constraints
- π§ͺ Test Driven: >95% test coverage with comprehensive BDD test suite
- iOS 15.0+ / macOS 12.0+
- Swift 5.9+
- Xcode 14.0+
Add the following dependency to your Package.swift
:
dependencies: [
.package(url: "https://github.com/tthew/anthropic-swift-sdk", from: "1.0.0")
]
Or add it through Xcode:
- File β Add Package Dependencies
- Enter the repository URL:
https://github.com/tthew/anthropic-swift-sdk
- Select the version you want to use
import AnthropicSDK
// Using API key directly
let client = try AnthropicClient(apiKey: "sk-ant-your-api-key")
// Using environment variable (ANTHROPIC_API_KEY)
let client = try AnthropicClient()
// Simple text message
let response = try await client.sendMessage("Hello, Claude!")
print(response.content.first?.text ?? "No response")
// With specific model and parameters
let response = try await client.sendMessage(
"Explain quantum computing",
model: .claude4Sonnet,
maxTokens: 1000
)
let stream = try await client.streamMessage("Write a story about space exploration")
for try await chunk in stream {
switch chunk {
case .messageStart(let start):
print("Message started: \(start.message.id)")
case .contentBlockStart:
print("Content starting...")
case .contentBlockDelta(let delta):
if case .textDelta(let text) = delta.delta {
print(text, terminator: "")
}
case .messageDelta(let delta):
// Message metadata updates (usage, stop reason)
if let usage = delta.usage {
print("\n[Tokens: \(usage.outputTokens)]")
}
if let stopReason = delta.delta.stopReason {
print("\n[Stopped: \(stopReason)]")
}
case .messageStop:
print("\nResponse complete!")
default:
break
}
}
Define tools and let Claude use them to solve complex problems:
import AnthropicSDK
// Define available tools
let tools = [
Tool(
name: "calculate",
description: "Perform mathematical calculations",
inputSchema: [
"type": "object",
"properties": [
"expression": [
"type": "string",
"description": "Mathematical expression to evaluate"
]
],
"required": ["expression"]
]
)
]
// Handle tool execution
let response = try await client.sendMessageWithTools(
"What is 15 * 23 + 45?",
tools: tools
) { toolName, input in
switch toolName {
case "calculate":
if let expression = input["expression"] as? String {
// Your calculation logic here
return "The result is: \(evaluateExpression(expression))"
}
return "Invalid expression"
default:
return "Unknown tool: \(toolName)"
}
}
Discover available models and their capabilities:
// List all available models
let modelList = try await client.models.list()
for model in modelList.data {
print("Model: \(model.id)")
print("Context Window: \(model.contextWindow)")
print("Supports Vision: \(model.supportsVision)")
print("Description: \(model.description)")
print("---")
}
// Get information about a specific model
let modelInfo = try await client.models.retrieve(.claude4Sonnet)
print("Model: \(modelInfo.id)")
print("Context Window: \(modelInfo.contextWindow) tokens")
// Get all Claude models (offline)
let allModels = await client.models.getAllClaudeModels()
// Get model recommendations
let fastModel = await client.models.recommendModel(
requiresVision: false,
preferSpeed: true
)
print("Recommended fast model: \(fastModel)")
let visionModel = await client.models.recommendModel(
requiresVision: true,
preferSpeed: false
)
print("Recommended vision model: \(visionModel)")
Access Claude's reasoning process:
let response = try await client.sendMessageWithThinking(
"Solve this logic puzzle: Three friends each have a different pet...",
thinkingMode: .extended
)
// Access the reasoning steps
if let thinkingSteps = response.thinking {
print("Claude's reasoning:")
for step in thinkingSteps {
print("- \(step.content)")
}
}
print("Final answer: \(response.content)")
Process multiple requests efficiently:
// Create batch requests
let requests = [
BatchRequest(
customId: "task_1",
method: .POST,
url: "/v1/messages",
body: CreateMessageRequest(
model: .claude3_5Sonnet,
messages: [.user("Summarize the importance of renewable energy")],
maxTokens: 500
)
),
BatchRequest(
customId: "task_2",
method: .POST,
url: "/v1/messages",
body: CreateMessageRequest(
model: .claude3_5Sonnet,
messages: [.user("Explain machine learning basics")],
maxTokens: 500
)
)
]
// Submit batch
let batch = try await client.batches.create(
CreateBatchRequest(requests: requests)
)
// Monitor progress
let updatedBatch = try await client.batches.retrieve(batch.id)
print("Status: \(updatedBatch.processingStatus)")
print("Progress: \(updatedBatch.requestCounts.succeeded)/\(updatedBatch.requestCounts.total)")
// Retrieve results when complete
if updatedBatch.isCompleted {
let results = try await client.batches.results(batch.id)
for result in results.data {
print("Request \(result.customId): \(result.isSuccess ? "β" : "β")")
}
}
Upload and manage files for Claude processing:
// Upload a document
let fileData = try Data(contentsOf: documentURL)
let uploadRequest = FileUploadRequest(
file: fileData,
filename: "analysis.pdf",
contentType: "application/pdf",
purpose: .document
)
let uploadResponse = try await client.files.upload(uploadRequest)
let file = uploadResponse.file
// Use file in a message
let response = try await client.messages.create(
model: .claude3_5Sonnet,
messages: [.user("Analyze this document and provide key insights")],
maxTokens: 1000,
files: [FileReference(fileId: file.id)]
)
// List all files
let filesList = try await client.files.list(purpose: .document)
print("Total files: \(filesList.data.count)")
// Download file content
let content = try await client.files.downloadContent(file.id)
Configure the client for different environments:
// Mobile-optimized configuration
let mobileClient = try AnthropicClient(
apiKey: "sk-ant-your-key",
configuration: .mobile
)
// Server environment configuration
let serverClient = try AnthropicClient(
apiKey: "sk-ant-your-key",
configuration: .server
)
// Custom configuration
let customConfig = ClientConfiguration(
connectionTimeout: 45,
maxConcurrentRequests: 8,
enableCaching: true,
enableRetry: true,
maxRetryAttempts: 3
)
let customClient = try AnthropicClient(
apiKey: "sk-ant-your-key",
configuration: customConfig
)
Model | Description | Context Window | Vision Support |
---|---|---|---|
claude4Opus |
Most intelligent model with hybrid reasoning | 200K tokens | β |
claude4Sonnet |
Advanced model with superior coding capabilities | 200K tokens | β |
claude3_5Sonnet |
Most intelligent, balanced performance | 200K tokens | β |
claude3_5Haiku |
Fastest, lightweight for everyday tasks | 200K tokens | β |
claude3Opus |
Most powerful for complex reasoning | 200K tokens | β |
claude3Sonnet |
Balanced intelligence and speed | 200K tokens | β |
claude3Haiku |
Fast and cost-effective | 200K tokens | β |
The SDK provides comprehensive error handling:
do {
let response = try await client.sendMessage("Hello, Claude!")
} catch AnthropicError.invalidAPIKey {
print("Please check your API key")
} catch AnthropicError.invalidParameter(let message) {
print("Invalid parameter: \(message)")
} catch HTTPError.rateLimited {
print("Rate limited - please retry after delay")
} catch HTTPError.serverError(let statusCode) {
print("Server error: \(statusCode)")
} catch {
print("Unexpected error: \(error)")
}
let config = ClientConfiguration(
connectionTimeout: 60, // Connection timeout (seconds)
resourceTimeout: 300, // Request timeout (seconds)
maxConcurrentRequests: 10, // Max simultaneous requests
enableCaching: false, // Response caching
enableRetry: true, // Automatic retries
maxRetryAttempts: 3, // Retry attempts
retryBaseDelay: 1.0 // Base retry delay (seconds)
)
let retryStrategy = RetryStrategy(
maxAttempts: 5,
baseDelay: 1.0,
maxDelay: 30.0,
retryableStatusCodes: [408, 429, 500, 502, 503, 504]
)
let circuitBreaker = CircuitBreaker(
failureThreshold: 5, // Failures before opening
recoveryTimeout: 60, // Seconds before retry
monitoringWindow: 300 // Window for failure counting
)
// Use connection pooling for multiple requests
let client = try AnthropicClient(apiKey: "sk-ant-your-key")
// Prefer batch operations for bulk processing
let batchRequests = messages.map { message in
BatchRequest(customId: message.id, method: .POST, url: "/v1/messages",
body: CreateMessageRequest(model: .claude3_5Sonnet, messages: [message]))
}
// Implement proper retry logic
func sendWithRetry(_ message: String, maxAttempts: Int = 3) async throws -> MessageResponse {
for attempt in 1...maxAttempts {
do {
return try await client.sendMessage(message)
} catch HTTPError.rateLimited {
if attempt < maxAttempts {
try await Task.sleep(nanoseconds: UInt64(pow(2.0, Double(attempt))) * 1_000_000_000)
continue
}
throw HTTPError.rateLimited
}
}
fatalError("Should not reach here")
}
// Handle streaming with proper error recovery and parsing resilience
func processStream(_ stream: MessageStream) async throws {
do {
for try await chunk in stream {
switch chunk {
case .contentBlockDelta(let delta):
// Process text content
if case .textDelta(let text) = delta {
print(text, terminator: "")
}
case .error(let streamError):
// Handle streaming errors gracefully
print("Stream error: \(streamError.localizedDescription)")
if streamError.error.type == "parsing_error" {
// Log detailed error for debugging
print("Parsing error details: \(streamError.error.message)")
// Continue processing other chunks
continue
} else {
// Handle other error types as needed
break
}
case .messageStop:
print("\nStream complete!")
break
default:
// Handle other chunk types
break
}
}
} catch {
// Handle stream interruption
print("Stream error: \(error)")
// Implement fallback or retry logic
}
}
The SDK includes comprehensive test coverage across multiple platforms:
# Run all tests
swift test
# Run with coverage
swift test --enable-code-coverage
# Run specific test suite
swift test --filter ClientInitializationTests
The CI pipeline automatically runs tests on iOS simulators for comprehensive platform coverage:
# Build for iOS Simulator (manual testing)
xcodebuild build \
-scheme AnthropicSDK \
-destination 'platform=iOS Simulator,name=iPhone 15,OS=latest' \
CODE_SIGNING_ALLOWED='NO'
# Run iOS tests (manual testing)
xcodebuild test \
-scheme AnthropicSDK \
-destination 'platform=iOS Simulator,name=iPhone 15,OS=latest' \
CODE_SIGNING_ALLOWED='NO'
Automated iOS Testing: GitHub Actions runs the full test suite on:
- iOS 15.0, 16.0, 17.0, and latest
- iPhone 14 and iPhone 15 simulators
- All iOS-compatible SDK functionality
Check out the Examples/
directory for complete sample applications:
- ChatApp: Simple chat interface with streaming
- ToolUseDemo: Advanced tool integration example
- BatchProcessor: Bulk processing demonstration
- FileAnalyzer: Document upload and analysis
If you encounter streaming parsing errors with certain models (especially Claude 3.5 Haiku):
The SDK now includes comprehensive streaming chunk support and improved parser resilience:
for try await chunk in stream {
switch chunk {
case .messageDelta(let delta):
// Handle message metadata updates (v1.1.3+)
if let usage = delta.usage {
print("Current tokens: \(usage.outputTokens)")
}
if let stopReason = delta.delta.stopReason {
print("Stop reason: \(stopReason)")
}
case .error(let streamError):
// StreamingErrorChunk now conforms to Error protocol
print("Stream error: \(streamError.localizedDescription)")
switch streamError.error.type {
case "parsing_error":
// Enhanced parsing error with detailed debugging info
print("Raw data causing issue: \(streamError.error.message)")
// You can choose to continue or fallback to non-streaming
continue
case "unknown_chunk_type":
// New chunk types from API updates
print("Unknown chunk type - may need SDK update")
continue
default:
// Handle other streaming errors
break
}
default:
// Process normal chunks
break
}
}
// Implement automatic fallback for parsing errors
do {
let stream = try await client.streamMessage("Your prompt", model: .claude3_5Haiku)
for try await chunk in stream {
if case .error(let streamError) = chunk,
streamError.error.type == "parsing_error" {
// Fallback to non-streaming for this model
throw StreamingFallbackError()
}
// Process successful chunks...
}
} catch {
// Use non-streaming as fallback
let response = try await client.sendMessage("Your prompt", model: .claude3_5Haiku)
print(response.content.first?.text ?? "")
}
If Claude 4 models (.claude4Opus
, .claude4Sonnet
) are not available after updating:
import AnthropicSDK
// Print version information
SDKVersion.printVersion()
// Quick check for Claude 4 support
print("Claude 4 Support: \(SDKVersion.hasClaude4Support)")
# Option 1: Delete derived data
rm -rf ~/Library/Developer/Xcode/DerivedData
# Option 2: Clear SPM cache
swift package reset
swift package clean
# Option 3: Force package resolution (in your project)
swift package resolve --force-resolved-versions
In Xcode:
- File β Package Dependencies
- Select "anthropic-swift-sdk"
- Right-click β Update Package
Or update to specific version in Package.swift
:
.package(url: "https://github.com/tthew/anthropic-swift-sdk", from: "1.1.2")
Expected output with Claude 4 support:
Anthropic Swift SDK v1.1.3
Commit: [latest]
Claude 4 Support: β
Available
Available Models:
- claude-opus-4-20250514
- claude-sonnet-4-20250514
- claude-3-5-sonnet-20241022
[... other models]
If you're still seeing older models only, ensure you're using version 1.1.3 or later.
We welcome contributions! Please see our Contributing Guide for details.
- Clone the repository
- Open
Package.swift
in Xcode - Run tests to ensure everything is working
- Make your changes following TDD methodology
- Ensure all tests pass and coverage remains >95%
This project uses GitHub Actions for continuous integration and deployment:
- Multi-Platform Testing: Runs on macOS 13/14 with Swift 5.9-5.10
- iOS Simulator Testing: Complete test suite runs on iOS 15.0, 16.0, 17.0, and latest (iPhone 14/15 simulators)
- Linux Compatibility: Basic compilation and core functionality tests
- Comprehensive Coverage: All 124+ test cases with >95% code coverage
- Performance Validation: Automated performance regression detection
- Example Validation: Ensures all example projects build successfully
- Code Quality: Automated validation of API consistency and documentation
- Breaking Change Detection: Analyzes public API changes for compatibility
- Security Scanning: Basic security pattern analysis for common issues
- Documentation Validation: Ensures documentation stays current with code changes
- Required Status Checks: All CI tests must pass before merge
- PR Review Process: Minimum 1 review required for all changes
- Automatic Validation: PRs are automatically validated for:
- Test coverage and passing status
- Changelog updates for significant changes
- Documentation completeness for new APIs
- Security pattern compliance
- Fork & Branch: Create a feature branch from
main
- Develop: Make changes following TDD methodology
- Test Locally: Run
swift test
to ensure all tests pass - Submit PR: Create pull request targeting
main
branch - CI Validation: Wait for automated checks to complete
- Code Review: Address any feedback from maintainers
- Merge: PR automatically merges when all checks pass and approved
The CI pipeline runs automatically on:
- Pull request creation/updates
- Pushes to
main
branch - Manual workflow triggers
See GitHub Actions for complete workflow configurations.
This project is licensed under the MIT License - see the LICENSE file for details.
This is an unofficial library. For official support or questions about the Anthropic API itself, please contact Anthropic directly through their official channels. This SDK is provided "as is" without warranty of any kind.