Swift SDK
ElevenAgents SDK: Swift 애플리케이션에 맞춤형 대화형 음성 에이전트를 배포하세요.
완전히 작동하는 예제로 빠르게 시작하려면 전체 Swift 퀵스타트 프로젝트를 확인하세요.
설치
Swift Package Manager를 사용하여 프로젝트에 ElevenLabs Swift SDK를 추가하세요.
패키지 종속성 추가
dependencies: [ .package(url: "https://github.com/elevenlabs/elevenlabs-swift-sdk.git",from: "2.0.0") ]
또는 Xcode에서 다음을 수행하세요.
- Xcode에서 프로젝트를 엽니다.
File>Add Package Dependencies...로 이동합니다.- 리포지토리 URL을 입력합니다:
https://github.com/elevenlabs/elevenlabs-swift-sdk.git - 버전 2.0.0 이상을 선택합니다.
사용자에게 마이크 접근을 설명하기 위해 Info.plist에 NSMicrophoneUsageDescription를 추가해야 합니다.
SDK에는 iOS 14.0 이상/macOS 11.0 이상 및 Swift 5.9 이상이 필요합니다.
빠른 시작
몇 줄만으로 간단한 대화를 시작하세요. 선택 사항으로, 대화를 사용자에게 매핑하기 위해 자체 최종 사용자 ID를 전달하는 것을 권장합니다.
import ElevenLabs// Start a conversation with your agentlet conversation = try await ElevenLabs.startConversation(agentId: "your-agent-id",userId: "your-end-user-id",config: ConversationConfig())// Observe conversation state and messagesconversation.$state.sink { state inprint("Connection state: \(state)")}.store(in: &cancellables)conversation.$messages.sink { messages infor message in messages {print("\(message.role): \(message.content)")}}.store(in: &cancellables)// Send messages and control the conversationtry await conversation.sendMessage("Hello!")try await conversation.toggleMute()await conversation.endConversation()
인증
인증하고 대화를 시작하는 방법은 두 가지입니다.
공개 에이전트
비공개 에이전트
공개 에이전트의 경우 에이전트 ID를 직접 사용하세요.
let conversation = try await ElevenLabs.startConversation(agentId: "your-public-agent-id",config: ConversationConfig())
핵심 기능
반응형 대화 관리
SDK는 반응형 UI 업데이트를 위해 @Published 속성을 갖춘 최신 Conversation 클래스를 제공합니다.
@MainActorclass ConversationManager: ObservableObject {@Published var conversation: Conversation?private var cancellables = Set<AnyCancellable>()func startConversation(agentId: String) async throws {let config = ConversationConfig(conversationOverrides: ConversationOverrides(textOnly: false))conversation = try await ElevenLabs.startConversation(agentId: agentId,config: config)setupObservers()}private func setupObservers() {guard let conversation else { return }// Monitor connection stateconversation.$state.sink { state in print("State: \(state)") }.store(in: &cancellables)// Monitor messagesconversation.$messages.sink { messages in print("Messages: \(messages.count)") }.store(in: &cancellables)}}
음성 및 텍스트 모드
// Voice conversation (default)let voiceConfig = ConversationConfig(conversationOverrides: ConversationOverrides(textOnly: false))// Text-only conversationlet textConfig = ConversationConfig(conversationOverrides: ConversationOverrides(textOnly: true))
오디오 제어
// Microphone controltry await conversation.toggleMute()try await conversation.setMuted(true)// Check microphone statelet isMuted = conversation.isMuted// Access audio tracks for advanced use caseslet inputTrack = conversation.inputTracklet agentAudioTrack = conversation.agentAudioTrack
클라이언트 도구
클라이언트 도구를 사용하면 대화 중 AI 에이전트가 호출할 수 있는 맞춤 함수를 등록할 수 있습니다. 새 SDK는 향상된 매개변수 처리와 오류 관리를 제공합니다.
도구 호출 처리
전체 매개변수 지원으로 에이전트의 도구 호출을 처리하세요.
private func handleToolCall(_ toolCall: ClientToolCallEvent) async {do {let parameters = try toolCall.getParameters()let result = await executeClientTool(name: toolCall.toolName,parameters: parameters)if toolCall.expectsResponse {try await conversation?.sendToolResult(for: toolCall.toolCallId,result: result)} else {conversation?.markToolCallCompleted(toolCall.toolCallId)}} catch {// Handle tool execution errorsif toolCall.expectsResponse {try? await conversation?.sendToolResult(for: toolCall.toolCallId,result: ["error": error.localizedDescription],isError: true)}}}// Example tool implementationfunc executeClientTool(name: String, parameters: [String: Any]) async -> [String: Any] {switch name {case "get_weather":guard let location = parameters["location"] as? String else {return ["error": "Missing location parameter"]}// Fetch weather datareturn ["temperature": "22°C", "condition": "Sunny"]case "send_email":guard let recipient = parameters["recipient"] as? String,let subject = parameters["subject"] as? String else {return ["error": "Missing required parameters"]}// Send email logicreturn ["status": "sent", "messageId": "12345"]default:return ["error": "Unknown tool: \(name)"]}}
ElevenLabs UI에서 클라이언트 도구로 에이전트를 설정해야 합니다. 설정 방법은 클라이언트 도구 문서를 참조하세요.
연결 상태 관리
서로 다른 연결 단계를 처리할 수 있도록 대화 상태를 모니터링하세요.
conversation.$state.sink { state inswitch state {case .idle:// Not connectedbreakcase .connecting:// Show connecting indicatorbreakcase .active(let callInfo):// Connected to agent: \(callInfo.agentId)breakcase .ended(let reason):// Handle disconnection: \(reason)breakcase .error(let error):// Handle error: \(error)break}}.store(in: &cancellables)
에이전트 상태 모니터링
에이전트가 듣고 있는지 또는 말하고 있는지 추적하세요.
conversation.$agentState.sink { state inswitch state {case .listening:// Agent is listening, show listening indicatorbreakcase .speaking:// Agent is speaking, show speaking indicatorbreak}}.store(in: &cancellables)
메시지 처리
텍스트 메시지를 보내고 대화를 모니터링하세요.
// Send a text messagetry await conversation.sendMessage("Hello, how can you help me today?")// Monitor all messages in the conversationconversation.$messages.sink { messages infor message in messages {switch message.role {case .user:print("User: \(message.content)")case .agent:print("Agent: \(message.content)")}}}.store(in: &cancellables)
오디오 정렬
동기화된 텍스트 표시에 사용할 문자 수준 타이밍 데이터를 모니터링하세요.
// Using the callbacklet config = ConversationConfig(onAudioAlignment: { alignment in// Character-level timing datafor (index, char) in alignment.chars.enumerated() {let startMs = alignment.charStartTimesMs[index]let durationMs = alignment.charDurationsMs[index]print("'\(char)' at \(startMs)ms for \(durationMs)ms")}})// Or observe the published propertyconversation.$latestAudioAlignment.compactMap { $0 }.sink { alignment in// Handle alignment updates}.store(in: &cancellables)
세션 관리
// End the conversationawait conversation.endConversation()// Check if conversation is activelet isActive = conversation.state.isActive
SwiftUI 통합
새 SDK를 사용하는 포괄적인 SwiftUI 예시입니다.
import SwiftUIimport ElevenLabsimport Combinestruct ConversationView: View {@StateObject private var viewModel = ConversationViewModel()var body: some View {VStack(spacing: 20) {// Connection statusText(viewModel.connectionStatus).font(.headline).foregroundColor(viewModel.isConnected ? .green : .red)// Chat messagesScrollView {LazyVStack(alignment: .leading, spacing: 8) {ForEach(viewModel.messages, id: \.id) { message inMessageBubble(message: message)}}}.frame(maxHeight: 400)// ControlsHStack(spacing: 16) {Button(viewModel.isConnected ? "End" : "Start") {Task {if viewModel.isConnected {await viewModel.endConversation()} else {await viewModel.startConversation()}}}.buttonStyle(.borderedProminent)Button(viewModel.isMuted ? "Unmute" : "Mute") {Task { await viewModel.toggleMute() }}.buttonStyle(.bordered).disabled(!viewModel.isConnected)Button("Send Message") {Task { await viewModel.sendTestMessage() }}.buttonStyle(.bordered).disabled(!viewModel.isConnected)}// Agent state indicatorif viewModel.isConnected {HStack {Circle().fill(viewModel.agentState == .speaking ? .blue : .gray).frame(width: 10, height: 10)Text(viewModel.agentState == .speaking ? "Agent speaking" : "Agent listening").font(.caption)}}}.padding()}}struct MessageBubble: View {let message: Messagevar body: some View {HStack {if message.role == .user { Spacer() }VStack(alignment: .leading) {Text(message.role == .user ? "You" : "Agent").font(.caption).foregroundColor(.secondary)Text(message.content).padding().background(message.role == .user ? Color.blue : Color.gray.opacity(0.3)).foregroundColor(message.role == .user ? .white : .primary).cornerRadius(12)}if message.role == .agent { Spacer() }}}}@MainActorclass ConversationViewModel: ObservableObject {@Published var messages: [Message] = []@Published var isConnected = false@Published var isMuted = false@Published var agentState: AgentState = .listening@Published var connectionStatus = "Disconnected"private var conversation: Conversation?private var cancellables = Set<AnyCancellable>()func startConversation() async {do {conversation = try await ElevenLabs.startConversation(agentId: "your-agent-id",config: ConversationConfig())setupObservers()} catch {print("Failed to start conversation: \(error)")connectionStatus = "Failed to connect"}}func endConversation() async {await conversation?.endConversation()conversation = nilcancellables.removeAll()}func toggleMute() async {try? await conversation?.toggleMute()}func sendTestMessage() async {try? await conversation?.sendMessage("Hello from the app!")}private func setupObservers() {guard let conversation else { return }conversation.$messages.assign(to: &$messages)conversation.$state.map { state inswitch state {case .idle: return "Disconnected"case .connecting: return "Connecting..."case .active: return "Connected"case .ended: return "Ended"case .error: return "Error"}}.assign(to: &$connectionStatus)conversation.$state.map { $0.isActive }.assign(to: &$isConnected)conversation.$isMuted.assign(to: &$isMuted)conversation.$agentState.assign(to: &$agentState)}}