Prerequisite: This guide assumes you have already completed the Installation from the SDK Mode guide. If not, complete those steps first.
How Host Mode Differs from SDK Mode
In Host Mode, your application manages the network connection to AvatarKit’s server-side SDK. The client SDK receives encoded messages and decodes them internally for playback — it does not connect to AvatarKit servers directly.
| Aspect | SDK Mode | Host Mode |
|---|
| Session Token | Required | Not required (client-side) |
| Network | Client SDK connects directly | Your app relays messages from Server SDK |
| Message Decoding | Internal | Internal (same) |
| Server SDK | Not needed | Required on your backend |
| Key Methods | start(), send(), close() | yield(audio), yield(animations) |
Host Mode requires AvatarKit’s server-side SDK on your backend. The data passed to yield() are encoded messages from the server SDK — not raw audio or animation data.
Quick Start
Initialize SDK
import AvatarKit
AvatarSDK.initialize(
appID: "your-app-id",
configuration: Configuration(
environment: .intl,
audioFormat: AudioFormat(sampleRate: 16000),
drivingServiceMode: .host, // Host mode
logLevel: .off
)
)
// No sessionToken needed for Host Mode
Load Avatar & Create View
let avatar = try await AvatarManager.shared.load(id: "avatar-id") { progress in
print("Loading: \(progress.fractionCompleted * 100)%")
}
let avatarView = AvatarView(avatar: avatar)
// Add to your view hierarchy
Set Up Callbacks
let controller = avatarView.controller
controller.onFirstRendering = {
print("First frame rendered")
}
controller.onConversationState = { state in
print("Conversation: \(state)")
}
controller.onError = { error in
print("Error: \(error.localizedDescription)")
}
Send Messages from Server SDK
// 1. Send encoded audio message — returns conversationID
let audioMessage: Data = ... // Encoded message from AvatarKit Server SDK
let conversationID = avatarView.controller.yield(audioMessage, end: false)
// 2. Send encoded animation messages with the SAME conversationID
let animationMessages: [Data] = ... // Encoded messages from Server SDK
avatarView.controller.yield(animationMessages, conversationID: conversationID)
// 3. Continue sending more audio messages
avatarView.controller.yield(moreMessages, end: false)
// 4. Mark end of conversation
avatarView.controller.yield(Data(), end: true)
Cleanup
avatarView.controller.interrupt()
API Reference
yield (Audio)
Sends an encoded audio message from the server SDK to the client SDK for decoding and playback. Returns a conversationID that links audio and animation messages.
@discardableResult
controller.yield(_ audioData: Data, end: Bool = false, audioFormat: AudioFormat? = nil) -> String
| Parameter | Type | Description |
|---|
audioData | Data | Encoded audio message from AvatarKit Server SDK |
end | Bool | Whether this is the last message for the current round |
audioFormat | AudioFormat? | Optional override for audio format |
| Returns | String | conversationID for this session |
yield (Animation)
Sends encoded animation messages from the server SDK. Must use the conversationID returned by yield(audio).
controller.yield(_ animations: [Data], conversationID: String)
| Parameter | Type | Description |
|---|
animations | [Data] | Encoded animation messages from Server SDK |
conversationID | String | Must match the active conversation |
Common Methods
These methods work the same as in SDK Mode:
controller.pause() // Pause audio + animation
controller.resume() // Resume playback
controller.interrupt() // Stop and terminate conversation
Event Callbacks
controller.onFirstRendering: (() -> Void)?
controller.onConversationState: ((ConversationState) -> Void)?
controller.onError: ((AvatarError) -> Void)?
onConnectionState is not available in Host Mode — there is no WebSocket connection to monitor.
ConversationId Management
The conversationID links audio and animation messages for synchronized playback:
- Call
yield(audioData) — it returns a conversationID
- Use that same
conversationID when calling yield(animations, conversationID:)
- Messages with a mismatched
conversationID will be silently discarded
Important: Always use the conversationID returned by yield(audio) when sending animation messages. Mismatched IDs cause messages to be silently dropped.
Fallback Mechanism
If you provide empty animation data (empty array), the SDK automatically enters audio-only mode for that session:
- Audio continues to play normally
- Once in audio-only mode, any subsequent animation data for that session is ignored
- The fallback mode is interruptible like normal playback
Complete Example
import AvatarKit
// Initialize in Host Mode
AvatarSDK.initialize(
appID: "your-app-id",
configuration: Configuration(
environment: .intl,
audioFormat: AudioFormat(sampleRate: 16000),
drivingServiceMode: .host,
logLevel: .off
)
)
// Load avatar
let avatar = try await AvatarManager.shared.load(id: "avatar-id")
let avatarView = AvatarView(avatar: avatar)
// Set up callbacks
avatarView.controller.onConversationState = { state in
print("State: \(state)")
}
avatarView.controller.onError = { error in
print("Error: \(error.localizedDescription)")
}
// Send encoded messages from your server
let audioMessage = fetchAudioMessageFromServer()
let conversationID = avatarView.controller.yield(audioMessage, end: false)
let animationMessages = fetchAnimationMessagesFromServer()
avatarView.controller.yield(animationMessages, conversationID: conversationID)
// End the conversation round
avatarView.controller.yield(Data(), end: true)