AI-Powered Voice Conversation iOS App
Built with SwiftUI and ElevenLabs Conversational AI SDK
- Overview
- Features
- Architecture
- Requirements
- Installation
- ElevenLabs Agent Setup
- Backend Configuration
- App Configuration
- Usage
- Troubleshooting
- Tech Stack
- Push Notifications
OpenClaw is a native iOS application that enables real-time voice conversations with AI agents powered by ElevenLabs Conversational AI. The app features a modern, immersive UI design with smooth animations, secure credential storage, and support for both public and private ElevenLabs agents.
- Real-time Voice Conversations - Talk naturally with AI agents using WebRTC technology
- Text Messaging - Optional text input for when voice isn't convenient
- Private Agent Support - Securely connect to private ElevenLabs agents with API key authentication
- Live Transcription - See conversation transcripts in real-time
- Animated Voice Visualizer - Beautiful orb animation that responds to agent state
- Secure Credential Storage - API keys stored safely in iOS Keychain
- Network Monitoring - Automatic detection of connectivity status
- Dark Mode Design - Elegant warm-toned dark interface inspired by Anthropic's design language
- Push Notifications - Receive notifications from OpenClaw Gateway via APNs
OpenClaw follows a clean MVVM (Model-View-ViewModel) architecture with clear separation of concerns.
OpenClaw/
├── App/
│ ├── AppState.swift # Global application state
│ ├── AppDelegate.swift # APNs registration callbacks
│ └── NotificationDelegate.swift # Foreground notification handling
├── Extensions/
│ └── Color+Theme.swift # Color palette and theming
├── Features/
│ ├── Conversation/
│ │ ├── ConversationView.swift # Main conversation UI
│ │ ├── ConversationViewModel.swift # Conversation business logic
│ │ ├── MessageBubbleView.swift # Chat message component
│ │ └── OrbVisualizerView.swift # Animated voice visualizer
│ └── Settings/
│ ├── SettingsView.swift # Settings UI
│ └── SettingsViewModel.swift # Settings business logic
├── Models/
│ └── ConversationTypes.swift # Data models and types
├── Services/
│ ├── AudioSessionManager.swift # Audio session configuration
│ ├── ConversationManager.swift # ElevenLabs SDK wrapper
│ ├── KeychainManager.swift # Secure credential storage
│ ├── NetworkMonitor.swift # Connectivity monitoring
│ ├── TokenService.swift # API token management
│ ├── PushNotificationManager.swift # APNs registration and permissions
│ └── GatewayNotificationService.swift # Device registration with Gateway
├── Assets.xcassets # Images, colors, app icon
└── OpenClawApp.swift # App entry point
Gateway/ # OpenClaw Gateway Plugin
├── index.ts # Plugin entry point
├── apns-notifier.ts # HTTP/2 APNs client
├── ios-hooks.ts # Device registration hooks
├── openclaw.plugin.json # Plugin manifest
├── README.md # Plugin documentation
└── SETUP_DGX_SPARK.md # Setup guide for DGX Spark
| Component | Responsibility |
|---|---|
| ConversationManager | Singleton that wraps the ElevenLabs SDK, manages conversation lifecycle, and publishes state changes |
| TokenService | Handles authentication with ElevenLabs API for private agents |
| KeychainManager | Securely stores and retrieves API keys and agent IDs |
| NetworkMonitor | Monitors network connectivity using NWPathMonitor |
| AudioSessionManager | Configures AVAudioSession for voice conversations |
| PushNotificationManager | Manages APNs registration, permissions, and device tokens |
| GatewayNotificationService | Registers device with OpenClaw Gateway for push notifications |
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ ConversationView│ ←→ │ConversationVM │ ←→ │ConversationMgr │
└─────────────────┘ └──────────────────┘ └─────────────────┘
↓
┌─────────────────┐
│ ElevenLabs SDK │
│ (LiveKit) │
└─────────────────┘
- iOS 17.0 or later
- Xcode 15.0 or later
- Swift 5.9 or later
- ElevenLabs Account with a configured AI agent
git clone https://github.com/acidoom/OpenClaw-app.git
cd OpenClaw-appopen OpenClaw.xcodeprojThe project uses Swift Package Manager. Xcode will automatically resolve dependencies when you open the project.
Dependencies:
- ElevenLabs Swift SDK - Conversational AI SDK
- LiveKit (transitive dependency) - WebRTC infrastructure
- Select the OpenClaw target in Xcode
- Go to Signing & Capabilities
- Select your Team
- Update the Bundle Identifier if needed
- Select your target device or simulator
- Press
Cmd + Rto build and run
This section walks you through creating and configuring an ElevenLabs Conversational AI agent from scratch.
- Go to ElevenLabs and sign up
- Verify your email and complete onboarding
- You'll need at least the Starter plan for Conversational AI features
- Navigate to Conversational AI in the left sidebar
- Click Create Agent or + New Agent
- Choose a template or start from scratch
| Setting | Description |
|---|---|
| Name | Give your agent a memorable name (e.g., "OpenClaw Assistant") |
| Language | Select the primary language (English recommended) |
| Voice | Choose from ElevenLabs' voice library or clone your own |
Configure your agent's personality and behavior:
You are OpenClaw, a helpful AI assistant. You are friendly, concise, and helpful.
Keep your responses brief and conversational since this is a voice interface.
Avoid using markdown, bullet points, or formatting that doesn't work well in speech.
Set what the agent says when a conversation starts:
Hello! I'm OpenClaw, your AI assistant. How can I help you today?
If you want to use your own LLM backend (like a local model or custom API):
- Go to Agent Settings → LLM
- Select Custom LLM
- Configure the completion endpoint:
┌─────────────────────────────────────────────────────────────┐
│ Custom LLM Setup │
├─────────────────────────────────────────────────────────────┤
│ Endpoint URL: https://your-server.com/v1/chat/completions │
│ API Key: your-api-key (if required) │
│ Model: your-model-name │
└─────────────────────────────────────────────────────────────┘
The endpoint must be OpenAI-compatible and accept:
POSTrequests with JSON body- Messages in the format:
[{"role": "user", "content": "..."}] - Return streaming responses with
choices[0].delta.content
- After creating your agent, go to Agent Settings
- Find the Agent ID (looks like:
agent_xxxxxxxxxxxx) - Copy this ID - you'll need it for the app
- Go to Agent Settings → Security
- Set visibility to Public
- Anyone with the Agent ID can connect (no API key needed in the app)
- Set visibility to Private
- You'll need an API key with
convai_writepermission - The app will authenticate via the token endpoint
OpenClaw can connect to a custom backend for enhanced functionality. This is optional but useful for:
- Using your own LLM models
- Adding custom business logic
- Implementing user authentication
- Logging and analytics
If you want to route requests through your own server (e.g., using Tailscale Funnel):
-
Install Tailscale on your server:
curl -fsSL https://tailscale.com/install.sh | sh -
Enable Funnel:
tailscale funnel 443 8080
-
Your endpoint will be available at:
https://your-machine.your-tailnet.ts.net -
Configure your LLM server to listen on port 8080
Create a server that implements the OpenAI chat completions API:
# Example using FastAPI
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Message(BaseModel):
role: str
content: str
class ChatRequest(BaseModel):
messages: list[Message]
stream: bool = True
@app.post("/v1/chat/completions")
async def chat_completions(request: ChatRequest):
# Your LLM logic here
# Return OpenAI-compatible streaming response
passYou can point ElevenLabs to any OpenAI-compatible endpoint:
| Provider | Endpoint |
|---|---|
| OpenAI | https://api.openai.com/v1/chat/completions |
| Azure OpenAI | https://{resource}.openai.azure.com/openai/deployments/{model}/chat/completions |
| Anthropic (via proxy) | Use a proxy that converts to OpenAI format |
| Local (Ollama) | http://localhost:11434/v1/chat/completions |
| Local (LM Studio) | http://localhost:1234/v1/chat/completions |
- In ElevenLabs, go to Agent Settings → LLM
- Select Custom LLM
- Enter your endpoint URL
- Add authentication headers if needed
- Test the connection
- Launch OpenClaw on your device
- Tap the gear icon (⚙️) to open Settings
- Enter your Agent ID in the Agent ID field
- Leave Private Agent toggle OFF
- Tap Save
- Tap Test Connection to verify
- Enter your Agent ID
- Enable the Private Agent toggle
- Enter your API Key
- Tap Save
- Tap Test Connection to verify
- Go to ElevenLabs API Keys
- Click Create API Key
- Important: Enable the
convai_writepermission - Copy the key immediately (it won't be shown again)
┌─────────────────────────────────────────────────────────────┐
│ API Key Permissions │
├─────────────────────────────────────────────────────────────┤
│ ☑ convai_write - Required for conversation tokens │
│ ☐ convai_read - Optional, for reading agent config │
│ ☐ text_to_speech - Not needed for OpenClaw │
└─────────────────────────────────────────────────────────────┘
┌─────────────┐ ┌─────────────────┐ ┌─────────────┐
│ OpenClaw │ │ ElevenLabs │ │ LiveKit │
│ App │ │ API │ │ Server │
└──────┬──────┘ └────────┬────────┘ └──────┬──────┘
│ │ │
│ POST /token │ │
│ (Agent ID + API Key) │ │
│────────────────────────►│ │
│ │ │
│ JWT Token │ │
│◄────────────────────────│ │
│ │ │
│ WebSocket Connect (JWT) │
│──────────────────────────────────────────────────►│
│ │ │
│ Audio Streams (WebRTC) │
│◄─────────────────────────────────────────────────►│
│ │ │
- Ensure your agent is configured in Settings
- Tap the coral waveform button to start
- Grant microphone permission when prompted
- Start speaking - the agent will respond
| Control | Action |
|---|---|
| Waveform Button | Start/stop conversation |
| Microphone Button | Mute/unmute your voice |
| Keyboard Button | Toggle text input mode |
The orb visualizer indicates the current state:
- Pulsing coral - Agent is listening
- Active animation - Agent is speaking
- Static gray - Disconnected
Error: "Timed out"
Causes & Solutions:
- Check your internet connection (WiFi/Cellular)
- Verify your Agent ID is correct (no extra spaces)
- For private agents, ensure API key has
convai_writepermission - Check if ElevenLabs services are operational
Error: "missing_permissions" or "invalid authorization token"
Solutions:
- Go to ElevenLabs API Keys
- Create a new API key with
convai_writepermission - Delete the old key from OpenClaw Settings
- Enter the new key and save
Checklist:
- Device volume is up
- Silent mode is off
- App has microphone permission (Settings → OpenClaw → Microphone)
- Try restarting the conversation
- Check if other apps can play audio
Checklist:
- Agent is properly configured in ElevenLabs dashboard
- Agent has a valid voice selected
- If using Custom LLM, verify the endpoint is reachable
- Check ElevenLabs dashboard for error logs
If you're using a custom LLM endpoint:
-
Test the endpoint manually:
curl -X POST https://your-endpoint/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"messages":[{"role":"user","content":"Hello"}],"stream":true}'
-
Verify response format matches OpenAI's streaming format
-
Check CORS headers if using a web-based proxy
-
Verify SSL certificate is valid (no self-signed certs in production)
| Technology | Purpose |
|---|---|
| SwiftUI | Declarative UI framework |
| Combine | Reactive state management |
| ElevenLabs SDK | Conversational AI integration |
| LiveKit | WebRTC infrastructure |
| Security.framework | Keychain credential storage |
| Network.framework | Connectivity monitoring |
| AVFoundation | Audio session management |
| UserNotifications | Push notification handling |
OpenClaw supports push notifications from the OpenClaw Gateway, allowing the AI agent to proactively reach out to users on their iOS devices.
┌─────────────┐ ┌─────────────────┐ ┌─────────────┐
│ OpenClaw │ │ OpenClaw │ │ Apple │
│ iOS App │ │ Gateway │ │ APNs │
└──────┬──────┘ └────────┬────────┘ └──────┬──────┘
│ │ │
│ Register Device Token │ │
│────────────────────────►│ │
│ │ │
│ │ Agent calls │
│ │ send_ios_notification │
│ │ │
│ │ HTTP/2 + JWT Auth │
│ │────────────────────────►│
│ │ │
│ Push Notification │ │
│◄─────────────────────────────────────────────────│
│ │ │
The Gateway/ folder contains the OpenClaw Gateway plugin for sending push notifications:
-
Copy plugin to Gateway server:
cp -r Gateway/ ~/.openclaw/extensions/ios-push-notifications/ -
Create APNs Key in Apple Developer Portal:
- Download the
.p8key file - Note the Key ID and Team ID
- Download the
-
Configure in
~/.openclaw/openclaw.json:{ "plugins": { "load": { "paths": ["~/.openclaw/extensions/ios-push-notifications"] }, "entries": { "ios-push-notifications": { "enabled": true, "config": { "apns": { "keyPath": "/path/to/AuthKey_XXXXXX.p8", "keyId": "YOUR_KEY_ID", "teamId": "YOUR_TEAM_ID", "bundleId": "carc.ai.OpenClaw", "sandbox": true } } } } } } -
Restart Gateway:
openclaw gateway restart
The OpenClaw agent can send notifications using the send_ios_notification tool:
"Send a push notification to device token ABC123... with title 'Hello' and body 'Your task is complete!'"
- Enable Push Notifications in Signing & Capabilities
- The app automatically requests notification permissions on launch
- Device token is displayed in Xcode console for testing
For detailed setup instructions, see Gateway/SETUP_DGX_SPARK.md.
This project is licensed under the MIT License - see the LICENSE file for details.
- ElevenLabs for the Conversational AI SDK
- LiveKit for WebRTC infrastructure
- Anthropic for design inspiration
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Made with ❤️ for AI-powered voice interactions
