Skip to main content

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

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 isPartial flag 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:

  1. Send text messages to interact with the AI Assistant via text