완전히 작동하는 예제로 빠르게 시작하려면 전체 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)}}