Real-time Transcript Updates
Overview
During AI Assistant conversations, the SDK provides real-time transcript updates that include both the caller's speech and the AI Assistant's responses. This allows you to display a live conversation transcript in your application.
Transcript Properties
The SDK provides two main ways to access transcript data:
Custom Publisher for Real-time Updates
// Subscribe to transcript updates using custom publisher
let cancellable = client.aiAssistantManager.subscribeToTranscriptUpdates { transcripts in
// Handle transcript updates
}
Current Transcript Access
// Get current transcripts as property (read-only)
let transcripts = client.aiAssistantManager.transcript
// Or via method
let transcripts = client.aiAssistantManager.getTranscriptions()
TranscriptionItem Structure
public struct TranscriptionItem {
public let id: String // Unique identifier
public let role: String // "user" or "assistant"
public let content: String // The transcribed text
public let timestamp: Date // When the item was created
public let isPartial: Bool // Whether this is a partial response
}
Setting Up Transcript Updates
Using Custom Publisher (Recommended)
class AIConversationViewController: UIViewController {
private let client = TxClient()
private var transcriptCancellable: TranscriptCancellable?
private var conversationTranscript: [TranscriptionItem] = []
override func viewDidLoad() {
super.viewDidLoad()
setupTranscriptListener()
}
private func setupTranscriptListener() {
transcriptCancellable = client.aiAssistantManager.subscribeToTranscriptUpdates { [weak self] transcript in
// Updates are already dispatched on main thread by the publisher
self?.updateConversationUI(transcript)
}
}
private func updateConversationUI(_ transcript: [TranscriptionItem]) {
conversationTranscript = transcript
// Update UI
conversationTableView.reloadData()
// Auto-scroll to bottom
if !transcript.isEmpty {
let indexPath = IndexPath(row: transcript.count - 1, section: 0)
conversationTableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
}
deinit {
transcriptCancellable?.cancel()
}
}
Processing Individual Transcript Items
private func updateConversationUI(_ transcript: [TranscriptionItem]) {
transcript.forEach { item in
switch item.role {
case "user":
print("User said: \(item.content)")
// Display user message in UI
addUserMessage(item.content, item.timestamp)
case "assistant":
print("Assistant said: \(item.content)")
// Display assistant message in UI
addAssistantMessage(item.content, item.timestamp, item.isPartial)
default:
break
}
}
}
Manual Transcript Access
You can also manually retrieve the current transcript at any time:
// Get current transcript via property
let currentTranscript = client.aiAssistantManager.transcript
// Or via method
let currentTranscript = client.aiAssistantManager.getTranscriptions()
// Process the transcript
currentTranscript.forEach { item in
print("\(item.role): \(item.content) (\(item.timestamp))")
}
Filtering Transcripts
The SDK provides convenient methods to filter transcripts by role or status:
// Get only user transcriptions
let userMessages = client.aiAssistantManager.getUserTranscriptions()
// Get only assistant transcriptions
let assistantMessages = client.aiAssistantManager.getAssistantTranscriptions()
// Get only partial (in-progress) transcriptions
let partialMessages = client.aiAssistantManager.getPartialTranscriptions()
// Get only final (completed) transcriptions
let finalMessages = client.aiAssistantManager.getFinalTranscriptions()
// Get transcriptions by specific role
let transcriptionsByRole = client.aiAssistantManager.getTranscriptionsByRole("user")
Handling Partial Responses
AI Assistant responses may come in chunks (partial responses). Handle these appropriately:
private func addAssistantMessage(_ content: String, _ timestamp: Date, _ isPartial: Bool) {
if isPartial {
// Update existing message or show typing indicator
updateLastAssistantMessage(content)
showTypingIndicator(true)
} else {
// Final message - hide typing indicator
showTypingIndicator(false)
finalizeAssistantMessage(content, timestamp)
}
}
Complete Example with UITableView
class ConversationViewController: UIViewController, UITableViewDataSource {
@IBOutlet weak var tableView: UITableView!
private let client = TxClient()
private var transcripts: [TranscriptionItem] = []
private var cancellable: TranscriptCancellable?
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
setupTranscripts()
}
private func setupTranscripts() {
cancellable = client.aiAssistantManager.subscribeToTranscriptUpdates { [weak self] transcripts in
DispatchQueue.main.async {
self?.transcripts = transcripts
self?.tableView.reloadData()
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return transcripts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let transcript = transcripts[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "TranscriptCell", for: indexPath)
cell.textLabel?.text = transcript.content
// Style based on role
if transcript.role == "user" {
cell.backgroundColor = .systemBlue
cell.textLabel?.textColor = .white
cell.textLabel?.textAlignment = .right
} else {
cell.backgroundColor = .systemGray6
cell.textLabel?.textColor = .label
cell.textLabel?.textAlignment = .left
}
// Show partial indicator
cell.textLabel?.alpha = transcript.isPartial ? 0.7 : 1.0
return cell
}
deinit {
cancellable?.cancel()
}
}
Delegate Pattern (Alternative Approach)
You can also use the delegate pattern to receive transcript updates:
class AIConversationViewController: UIViewController, AIAssistantManagerDelegate {
private let client = TxClient()
override func viewDidLoad() {
super.viewDidLoad()
client.aiAssistantManager.delegate = self
}
// MARK: - AIAssistantManagerDelegate
func onTranscriptionUpdated(_ transcriptions: [TranscriptionItem]) {
// Handle transcript updates
updateConversationUI(transcriptions)
}
func onWidgetSettingsUpdated(_ settings: WidgetSettings) {
// Handle widget settings updates
print("Widget settings updated: \(settings)")
}
func onAIConversationMessage(_ message: [String: Any]) {
// Handle raw AI conversation messages
print("AI message received: \(message)")
}
func onAIAssistantConnectionStateChanged(isConnected: Bool, targetId: String?) {
// Handle connection state changes
print("AI Assistant connected: \(isConnected), targetId: \(targetId ?? "none")")
}
func onRingingAckReceived(callId: String) {
// Handle ringing acknowledgment
print("Ringing ack for call: \(callId)")
}
}
Widget Settings Access
Access AI conversation widget settings:
// Get current widget settings
if let widgetSettings = client.aiAssistantManager.widgetSettings {
// Use widget settings to configure UI
print("Widget settings: \(widgetSettings)")
}
Connection State Monitoring
Monitor the AI Assistant connection state:
// Check if AI Assistant is connected
if client.aiAssistantManager.isAIAssistantConnected {
print("AI Assistant is connected")
}
// Get the connected target ID
if let targetId = client.aiAssistantManager.connectedTargetId {
print("Connected to target: \(targetId)")
}
Clearing Transcripts
You can clear transcripts manually if needed:
// Clear all transcriptions
client.aiAssistantManager.clearTranscriptions()
// Clear transcriptions by specific role
client.aiAssistantManager.clearTranscriptionsByRole("user")
client.aiAssistantManager.clearTranscriptionsByRole("assistant")
// Clear all AI Assistant data (including transcripts and widget settings)
client.aiAssistantManager.clearAllData()
Important Notes
- AI Assistant Only: Transcript updates are only available during AI Assistant conversations initiated through
anonymousLogin - Real-time Updates: Transcripts update in real-time as the conversation progresses
- Partial Responses: Assistant responses may come in chunks - handle
isPartialflag appropriately - Memory Management: Transcripts are automatically cleared when calls end or when disconnecting
- Thread Safety: Publisher updates are dispatched on the main thread automatically
- Multiple Subscribers: You can have multiple subscribers to the same transcript updates
Error Handling
cancellable = client.aiAssistantManager.subscribeToTranscriptUpdates { [weak self] transcripts in
guard !transcripts.isEmpty else {
print("Received empty transcript array")
return
}
self?.updateConversationUI(transcripts)
}
// Always cancel subscriptions when done
deinit {
cancellable?.cancel()
}
Next Steps
After setting up transcript updates:
- Send text messages to interact with the AI Assistant via text