ChatKit provides multiple API levels to suit different use cases. This guide explains when to use each level and how they differ.
ChatKit offers three ways to integrate:
- High-Level APIs (Recommended) - Simple, ready-made components
- Low-Level APIs (Advanced) - Maximum flexibility, more boilerplate
- Provider Mechanism - Customize framework behavior without changing code
Best for: Most applications, rapid development, standard chat UI
The high-level APIs provide ready-made components that handle most common use cases with minimal code.
Manages runtime lifecycle safely. Create once at app launch.
let config = NeuronKitConfig.default(serverURL: serverURL)
.withUserId("user-123")
let coordinator = ChatKitCoordinator(config: config)Tracks multiple conversations automatically. Optional but recommended for multi-session apps.
let manager = ChatKitConversationManager()
manager.attach(runtime: coordinator.runtime)Ready-made chat UI with message rendering, input composer, and all interactions.
let chatVC = ChatKitConversationViewController(
record: record,
conversation: conversation,
coordinator: coordinator,
configuration: .default
)Ready-made conversation list with search, swipe actions, and selection handling.
let listVC = ChatKitConversationListViewController(
coordinator: coordinator,
configuration: .default
)The Simple demo app demonstrates high-level APIs:
// 1. Initialize once at app launch
let coordinator = ChatKitCoordinator(config: config)
// 2. Create conversation when user requests it
let (record, conversation) = try await coordinator.startConversation(
agentId: agentId,
title: nil,
agentName: "My Agent"
)
// 3. Show ready-made chat UI
let chatVC = ChatKitConversationViewController(
record: record,
conversation: conversation,
coordinator: coordinator,
configuration: config
)Benefits:
- ✅ Minimal code (20-30 lines for basic chat)
- ✅ Handles all UI interactions automatically
- ✅ Consistent behavior and styling
- ✅ Built-in features (search, swipe actions, etc.)
- ✅ Safe lifecycle management
See:
demo-apps/iOS/Simple/- Swift exampledemo-apps/iOS/SimpleObjC/- Objective-C example
Best for: Custom UI requirements, maximum control, non-standard layouts
The low-level APIs give you direct access to the underlying components, allowing complete customization at the cost of more boilerplate code.
Access the runtime directly for custom orchestration.
let runtime = coordinator.runtime
// Direct runtime manipulationManual UI binding for custom chat implementations.
let hosting = ChatHostingController()
let adapter = ChatKitAdapter(chatView: hosting.chatView)
conversation.bindUI(adapter)Build your own UI using framework primitives.
Use low-level APIs when you need:
- Custom message rendering
- Non-standard UI layouts
- Specialized interaction patterns
- Integration with existing custom UI frameworks
Advantages:
- ✅ Complete control over UI and behavior
- ✅ Can integrate with any UI framework
- ✅ Maximum flexibility
Disadvantages:
- ❌ Significantly more code (200+ lines vs 20-30)
- ❌ Must handle lifecycle manually
- ❌ More boilerplate (binding, unbinding, state management)
- ❌ Must implement features yourself (search, actions, etc.)
- ❌ Higher maintenance burden
A low-level implementation typically involves:
- Creating and managing
ChatHostingController - Manually binding/unbinding
ChatKitAdapter - Implementing custom UI components
- Handling all lifecycle events
- Managing conversation state manually
Note: The framework provides low-level APIs for maximum flexibility, but most developers should use high-level APIs. Low-level APIs are verbose and require significant boilerplate code.
Best for: Customizing framework behavior without modifying framework code
Providers allow you to inject custom logic into the framework at specific points.
Attach contextual information (location, calendar events, etc.) to messages via the composer UI.
import FinClipChatKit
import ConvoUI
class LocationContextProvider: ConvoUIContextProvider {
func provideContext(completion: @escaping (ConvoUIContext?) -> Void) {
// Your location logic
let context = ConvoUIContext(
title: "Current Location",
content: "Lat: 37.7749, Lng: -122.4194"
)
completion(context)
}
}
// Register in ChatKitConversationConfiguration
var config = ChatKitConversationConfiguration.default
config.contextProvidersProvider = {
MainActor.assumeIsolated {
[
ConvoUIContextProviderBridge(provider: LocationContextProvider()),
ConvoUIContextProviderBridge(provider: CalendarContextProvider())
]
}
}#import <ConvoUI/ConvoUI.h>
@interface MyLocationProvider : NSObject <FinConvoComposerContextProvider>
@end
@implementation MyLocationProvider
- (void)provideContextWithCompletion:(void (^)(FinConvoContext * _Nullable))completion {
// Your location logic
FinConvoContext *context = [[FinConvoContext alloc] initWithTitle:@"Location"
content:@"Lat: 37.7749, Lng: -122.4194"];
completion(context);
}
@end
// Register via ChatKitConversationConfigurationProvide custom Automatic Speech Recognition for press-and-talk voice input.
#import <ConvoUI/ConvoUI.h>
@interface MyASRProvider : NSObject <FinConvoSpeechRecognizer>
@end
@implementation MyASRProvider
- (void)transcribeAudio:(NSURL *)audioFileURL
completion:(void (^)(NSString * _Nullable, NSError * _Nullable))completion {
// Your ASR implementation (e.g., OpenAI Whisper, Google Speech-to-Text)
// Process audio and return transcribed text
completion(transcribedText, nil);
}
- (void)cancelTranscription {
// Cancel any ongoing requests
}
@end
// Register via ChatKitConversationConfigurationDefault: If no ASR provider is specified, ChatKit uses Apple's Speech framework.
Customize how conversation titles are generated.
class CustomTitleProvider: ConversationTitleProvider {
func shouldGenerateTitle(
sessionId: UUID,
messageCount: Int,
currentTitle: String?
) async -> Bool {
// Return true when title should be generated
return messageCount >= 3 && currentTitle == nil
}
func generateTitle(messages: [NeuronMessage]) async throws -> String? {
// Your title generation logic (e.g., LLM call)
// Use first few messages to generate title
return try await callLLMForTitle(messages: messages)
}
}
// Register when creating ChatKitConversationManager
let manager = ChatKitConversationManager(titleProvider: CustomTitleProvider())#import <FinClipChatKit/FinClipChatKit-Swift.h>
@interface MyTitleProvider : NSObject <CKTConversationTitleProvider>
@end
@implementation MyTitleProvider
- (void)shouldGenerateTitleForSessionId:(NSString *)sessionId
messageCount:(NSInteger)messageCount
currentTitle:(NSString *)currentTitle
completion:(void (^)(BOOL))completion {
// Return YES when title should be generated
completion(messageCount >= 3 && currentTitle == nil);
}
- (void)generateTitleForSessionId:(NSString *)sessionId
messages:(NSArray *)messages
completion:(void (^)(NSString * _Nullable, NSError * _Nullable))completion {
// Your title generation logic
// messages is an array of dictionaries with message data
[self callLLMForTitle:messages completion:^(NSString *title, NSError *error) {
completion(title, error);
}];
}
@end
// Register when creating CKTConversationManager
CKTConversationManager *manager = [[CKTConversationManager alloc] initWithTitleProvider:[[MyTitleProvider alloc] init]];Default: If no title provider is specified, ChatKit extracts a title from the first user message.
- ✅ You want standard chat UI
- ✅ You want rapid development
- ✅ You're building a typical chat app
- ✅ You want minimal code
Start here: Most developers should use high-level APIs.
⚠️ You need completely custom UI⚠️ You're integrating with existing custom frameworks⚠️ You have specialized interaction requirements⚠️ You're willing to write significantly more code
Warning: Low-level APIs require 10x more code and manual lifecycle management.
- ✅ You want to customize specific behaviors
- ✅ You need custom context, ASR, or title generation
- ✅ You want to extend framework without modifying it
Note: Providers work with both high-level and low-level APIs.
If you're currently using low-level APIs, migrating to high-level APIs is straightforward:
- Replace
ChatHostingController+ChatKitAdapterwithChatKitConversationViewController - Use
ChatKitCoordinator.startConversation()instead of manual conversation creation - Use
ChatKitConversationListViewControllerinstead of custom list UI - Remove manual binding/unbinding code
Result: 90% less code, same functionality.
See: demo-apps/iOS/Simple/ (Swift) and demo-apps/iOS/SimpleObjC/ (Objective-C)
See: demo-apps/iOS/MyChatGPT/ (conceptual reference - demonstrates pattern, not recommended for most developers)
- Quick Start Guide - Get started with high-level APIs (Swift & Objective-C)
- Getting Started Guide - Detailed walkthrough with explanations
- Component Embedding Guide - Learn how to embed components (Swift & Objective-C examples)
- Swift: Swift Developer Guide - Comprehensive patterns and examples
- Objective-C: Objective-C Developer Guide - Complete Objective-C guide with API reference
- Context Providers Guide - Implementing custom context providers
- Configuration Guide - Complete configuration reference
Recommendation: Start with high-level APIs. They cover 95% of use cases with minimal code. Only use low-level APIs if you have specific requirements that high-level APIs cannot meet.
Language Support:
- Swift: Full support with async/await and Combine
- Objective-C: Full support via
CKT-prefixed wrapper classes with delegate-based patterns