// Import StreamChat framework.
import StreamChat
// for sake of simplicity we are extending ChatClient and add a static var `shared`
extension ChatClient {
static var shared: ChatClient!
}
// API key can be found on the dashboard: https://getstream.io/dashboard/
let config = ChatClientConfig(apiKey: .init("{{ api_key }}"))
let userID = "leia_organa"
// Create an instance of ChatClient and share it using the singleton
ChatClient.shared = ChatClient(config: config)
// Option A: with expiring token
// The `tokenProvider` closure will be called again when the token is expired
ChatClient.shared.connectUser(
userInfo: .init(id: userID),
tokenProvider: { providerResult in
loadChatToken(completion: providerResult)
},
completion: { error in
if let error = error {
print("Connection failed with: \(error)")
} else {
// User successfully connected
}
}
)
// or alternatively, using the async-await method
let connectedUser = try await ChatClient.shared.connectUser(
userInfo: .init(id: userID),
tokenProvider: { providerResult in
loadChatToken(completion: providerResult)
}
)
// An example of a token provider
func loadChatToken(completion: @escaping (Result<Token, Error>) -> Void) {
NetworkingLayer.getChatToken() { token in
do {
let token = try Token(rawValue: token)
completion(.success(token))
} catch {
completion(.failure(error))
}
}
}
// Option B: with a non-expiring token
// You can generate the token for this user from https://getstream.io/chat/docs/ios-swift/token_generator/?language=swift
let token: Token = "{{ chat_user_token }}"
/// Connect the user using a closure based method
ChatClient.shared.connectUser(
userInfo: .init(id: userID),
token: token
) { error in
if let error = error {
print("Connection failed with: \(error)")
} else {
// User successfully connected
}
}
// or alternatively, using the async-await version
let connectedUser = try await ChatClient.shared.connectUser(
userInfo: .init(id: userID),
token: token
)
iOS Introduction
Before reviewing the Chat API docs, we recommend having a look at the tutorials and sample apps.
We provide a low-level and two UI components SDKs: UIKit and SwiftUI. UI components SDKs provide a ready-made UI with a aim of having high level of customisation and reusability.
Main Features
UIKit
andSwiftUI
SDKs use native patterns and paradigms from respective UI frameworks: The API follows the design of native system SDKs. It makes integration with your existing code easy and familiar.First-class support for
Combine
andStructured Concurrency
: Refer to getting started guides for Combine and Structured Concurrency.Offline support: Browse channels and send messages while offline.
Familiar behaviour : The UI elements are good platform citizens and behave like native elements; they respect
tintColor
,layoutMargins
, light/dark mode, dynamic font sizes, etc.Swift native API: Uses Swift’s powerful language features to make the SDK usage easy and type-safe.
Fully open source implementation: You have access to the source code of our SDKs on GitHub: UIKit and SwiftUI.
Supports iOS 13:+ We proudly support older versions of iOS, so your app can stay available to almost everyone.
Overview
Our SDKs provide 3 different ways for integrating with Your app: low level client (LLC), UIKit SDK, and SwiftUI SDK. LLC is a data layer responsible of managing offline state and handling API requests. Use it for building fully custom UIs. UIKit and SwiftUI SDKs come with ready-made UI components built using respective UI frameworks. UI components are highly customisable and reusable and therefore, are the best way for bringing the chat UI to Your app. To learn how to use our pre-built UI components, see the iOS Chat Tutorial or read the UI SDK docs.
If you want to learn the basics of using our low-level client without the UI components, keep reading.
Installation
You can add Stream Chat to your Xcode project using Swift Package Manager, CocoaPods, and XCFrameworks. Please head over to our installation documentation hereand start building a world class chat UI.
Chat Client
Let’s get started by initializing the client and setting the current user:
The above snippet is for an in-browser or mobile integration. Server-side API calls are a little different, but this is covered in detail later in the documentation.
Channels
Let’s continue by initializing your first channel. A channel contains messages, a list of people that are watching the channel, and optionally a list of members (for private conversations). The example below shows how to set up a channel to support chat for a group conversation using completion handler based controllers and the async-await supported state-layer:
/// 1: Create a `ChannelId` that represents the channel you want to create.
let channelId = ChannelId(type: .messaging, id: "general")
/// 2: Use the `ChatClient` to create a `ChatChannelController` with the `ChannelId`.
let channelController = try chatClient.channelController(
createChannelWithId: channelId,
extraData: ["info": .string("Hello")]
)
/// 3: Call `ChatChannelController.synchronize` to create the channel.
channelController.synchronize { error in
if let error = error {
/// 4: Handle possible errors
print(error)
}
}
/// 1: Create a `ChannelId` that represents the channel you want to create.
let channelId = ChannelId(type: .messaging, id: "general")
/// 2: Use the `ChatClient` to create a `Chat` with the `ChannelId`.
let chat = try chatClient.makeChat(
with: channelId,
extraData: ["info": .string("Hello")]
)
/// 3: Call `Chat.get(watch:)` to create the channel.
try await chat.get(watch: true)
The first two arguments are the Channel Type and the Channel ID ( messaging
and general
in this case). You can also use createDirectMessageChannelWith
creator to create a channel without a cid, cid will be generated based on the members by the backend. The channel type controls the settings we’re using for this channel.
There are 5 default types of channels:
- livestream
- messaging
- team
- gaming
- commerce
These five options above provide you with the most sensible defaults for those use cases. You can also define custom channel types if Stream Chat defaults don’t work for your use-case.
The third argument is an object containing the channel data (extraData
). You can add as many custom fields as you would like as long as the total size of the object is less than 5KB.
Messages
Now that we have the channel set up, let’s send our first chat message:
channelController.createNewMessage(
text: "Hello",
extraData: ["info": .string("secret message")]
) { result in
switch result {
case .success(let messageId):
/// 2: Handle success
print(messageId)
case .failure(let error):
/// 3: Handle errors
print(error)
}
}
let sentMessage = try await chat.sendMessage(
with: "Hello",
extraData: ["info": .string("secret message")]
)
Similar to users and channels, the send method allows you to add custom fields. When you send a message to a channel, Stream Chat automatically broadcasts it to all the people that are watching this channel in real-time.
This is how you can listen to message list changes on the client-side:
//> import UIKit
//> import StreamChat
/// * Delegates *
class ViewController: UIViewController, ChatChannelControllerDelegate {
func channelController(
_ channelController: ChatChannelController,
didUpdateMessages changes: [ListChange<ChatMessage>]
) {
// animate the changes to the message list
}
}
let viewController = ViewController()
channelController.delegate = viewController
// Observing message list changes using the state-layer
chat.state.$messages
.sink { messages in
// updated array of `messages`
}
.store(in: &cancellables)
}
Conclusion
Now that you understand the building blocks of a fully functional chat integration, let’s move on to the next sections of the documentation, where we dive deeper into details on each of the available resources and the methods for interacting with them.