Integration of Stream Chat SDK into Mobile Application
Stream Chat differs from SendBird architecturally: API built on event-driven model (WebSocket + event-driven state), SDK provides ready SwiftUI/Compose components with deep customization via subclassing and view factories. Binary size smaller than SendBird — iOS around 8 MB, Android AAR ~6 MB. Let's break down integration from scratch without "Hello World" from docs.
Initialization and Tokens
Stream uses JWT. Client never generates token independently — only your backend:
POST /api/stream/token
{ "user_id": "user_123" }
→ { "token": "eyJ..." }
On backend token created via stream_chat.create_token(user_id) (Python/Node SDK). On client:
// iOS
let client = ChatClient(config: ChatClientConfig(apiKeyString: "YOUR_KEY"))
let token = try Token(rawValue: "eyJ...")
client.connectUser(userInfo: .init(id: userId), token: token) { error in ... }
// Android
val client = ChatClient.Builder("YOUR_KEY", context).build()
client.connectUser(User(id = userId), token).enqueue { result -> ... }
Token refresh: Stream SDK calls TokenProvider when token expires. Implement TokenProvider (iOS: closure-based, Android: TokenProvider interface) — there make request to your API and return new token. Without this user gets dropped from chat after token TTL.
Channels: Creation and Subscription
Stream uses combination type:id for channel identification. Types — messaging, livestream, team, commerce, gaming — affect default permissions.
// Get or create 1-to-1 channel
let channelId = ChannelId(type: .messaging, id: "user1_user2")
let controller = client.channelController(
createChannelWithId: channelId,
members: [userId, targetId],
isCurrentUserMember: true
)
controller.synchronize { error in ... }
synchronize() — key call. It pulls history, subscribes to realtime events and synchronizes local state. Without it channel creates but events don't arrive.
On Android:
val channelClient = client.channel(channelType = "messaging", channelId = "user1_user2")
channelClient.create(memberIds = listOf(userId, targetId)).enqueue { result -> ... }
SwiftUI UIComponents vs Custom UI
Stream provides ChatChannelView, MessageListView, MessageComposerView from StreamChatSwiftUI package. Customization — via ViewFactory:
class CustomViewFactory: DefaultViewFactory {
func makeMessageAvatarView(for userInfo: UserAvatarData) -> some View {
// Your custom avatar
CustomAvatarView(imageUrl: userInfo.imageURL)
}
}
// Inject:
Utils.shared.viewFactory = CustomViewFactory()
Cleaner than full custom UI — 80% behavior (swipe, reactions, threads) comes free, customize only appearance. Full custom UI makes sense only if design incompatible with Stream component model.
For Android similarly: MessageListView and MessageComposerView in XML or Compose, customization via AttachmentFactoryManager and MessageListViewModelFactory.
Reactions and Threads
Stream supports reactions natively via message.reactionScores and addReaction / deleteReaction endpoint. Threads (replies to message) — via controller.createNewReply. In ready components this works out of box; for custom UI need to implement ThreadController.
Push Notifications
Stream uses own notification provider on top of APNs/FCM. Registration:
// iOS — after receiving APNs token
chatClient.currentUserController().addDevice(.apns(token: deviceToken))
// Android
FirebaseMessaging.getInstance().token.addOnSuccessListener { token ->
client.addDevice(Device(token = token, pushProvider = PushProvider.FIREBASE)).enqueue()
}
Stream independently sends notifications on new messages in channels where user is member. In dashboard configure notification templates. Deeplink — via CKNNotificationInfo (iOS) or RemoteMessage.data (Android) handling.
Offline Cache
iOS SDK uses CoreData under hood, Android — Room. Enabled automatically when isLocalStorageEnabled = true in configuration (default — true). On connection restore SDK automatically syncs missed events via WebSocket health check mechanism.
Workflow
Register Stream app and configure keys → implement token endpoint on backend → SDK integration → choose between components and custom UI → push notifications → test reconnect/offline scenarios.
Timeline
With ready StreamChatSwiftUI / StreamChatUI components — 3-4 days. Fully custom UI on Core SDK — 6-8 days. Cost calculated individually.







