iOS Introduction
Confused about "iOS Introduction"?
Let us know how we can improve our documentation:
Before reviewing the Chat API docs, we recommend having a look at the tutorials and sample apps.
-
iOS Chat SDK
Start Tutorial →
The interactive API tour is the fastest way to learn how Stream’s Chat API works. Be sure to check that out if you haven’t done so already.
CHAT API TOURWith Stream Chat you can add a feature rich experience to your app. Leverage our front end components and APIs to build the type of chat you want.
The interactive API tour is the fastest way to learn how Stream’s Chat API works. Be sure to check that out if you haven’t done so already.
Chat API TouriOS Main Features
Copied!Confused about "iOS Main Features"?
Let us know how we can improve our documentation:
Uses
UIKit
patterns and paradigms: The API follows the design of native system SKDs. It makes integration with your existing code easy and familiar.First-class support for
SwiftUI
andCombine
: Built-it wrappers make using the SDK with the latest Apple UI framework a seamless experience. (coming 4/2021)Offline support: Browse channels and send messages while offline.
Familiar behavior: 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 comple source code of the SDK on GitHub.
Supports iOS 11+, Swift 5.2: We proudly support older versions of iOS, so your app can stay available to almost everyone.
UI Implementation
Copied!Confused about "UI Implementation"?
Let us know how we can improve our documentation:
StreamChatUI provides local database logic and API calls, provided by the StreamChat dependency, as well as UIKit components. Use StreamChatUI if you want a ready-made fully-featured UI with some customizability options. To learn how to use our pre-built UI components, see the iOS Chat Tutorial.

If you want to learn the basics of using our low-level client without the UI components, keep reading...
Chat Client
Copied!Confused about "Chat Client"?
Let us know how we can improve our documentation:
Let's get started by initializing the client and setting the current user:
1
2
3
4
5
6
7
8
9
const client = new StreamChat("YOUR_API_KEY");
await client.setUser(
{
id: 'jlahey',
name: 'Jim Lahey',
image: 'https://i.imgur.com/fR9Jz14.png',
},
"CHAT_USER_TOKEN",
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
String apiKey = "YOUR_API_KEY";
String token = "CHAT_USER_TOKEN";
Context context = getApplicationContext();
ChatClient client = new ChatClient.Builder(apiKey, context).build();
User user = new User();
user.setId("jlahey");
user.putExtraValue("image", "https://bit.ly/321RmWb");
user.putExtraValue("name", "Jim Lahey");
client.setUser(user, token, new InitConnectionListener() {
@Override
public void onSuccess(@NotNull ConnectionData data) {
final User user = data.getUser();
final String connectionId = data.getConnectionId();
Toast.makeText(context, "Logged in successfully as " + user.getExtraValue("name", ""), Toast.LENGTH_SHORT).show();
}
@Override
public void onError(@NotNull ChatError error) {
error.getCause().printStackTrace();
}
});
1
2
3
4
5
6
7
8
9
/// 1: Create a static token provider. Use it for testing purposes.
let token = Token("CHAT_USER_TOKEN")
let tokenProvider = TokenProvider.static(token)
/// 2: Create a `ChatClientConfig` with the API key.
let config = ChatClientConfig(apiKeyString: "YOUR_API_KEY")
/// 3: Create a `ChatClient` instance with the config and the token provider.
chatClient = ChatClient(config: config, tokenProvider: tokenProvider)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
val apiKey = "YOUR_API_KEY"
val token = "CHAT_USER_TOKEN"
val context = getApplicationContext()
val client = ChatClient.Builder(apiKey, context).build()
val user = User("jlahey")
user.extraData["image"] = "https://bit.ly/321RmWb"
user.extraData["name"] = "Jim Lahey"
client.setUser(user, token, object : InitConnectionListener() {
override fun onSuccess(data: ConnectionData) {
val user = data.user
val connectionId = data.connectionId
Toast.makeText(context, "Logged in successfully as ${user.name}", Toast.LENGTH_SHORT).show()
}
override fun onError(error: ChatError) {
error.cause?.printStackTrace()
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
import 'package:stream_chat/stream_chat.dart';
// create a client with log-level INFO
final client = Client("YOUR_API_KEY", logLevel: Level.INFO);
// init the user object, note how you can specify custom fields as well
final user = User(id: "jlahey", extraData: {
'name': 'Jim Lahey',
'image': 'https://i.imgur.com/fR9Jz14.png',
});
// sets the current user, from now on the client can be used to query channels and receive events
await client.setUser(user, "CHAT_USER_TOKEN");
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
Copied!Confused about "Channels"?
Let us know how we can improve our documentation:
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:
1
2
3
4
5
6
const channel = client.channel('messaging', 'travel', {
name: 'Awesome channel about traveling',
});
// fetch the channel state, subscribe to future updates
const state = await channel.watch();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ChannelClient channelClient = client.channel("messaging", "travel");
HashMap extraData = new HashMap<String, Object>();
extraData.put("name", "Awesome channel about traveling");
// Watching a channel"s state
QueryChannelRequest request = new QueryChannelRequest()
.withData(extraData)
.withMessages(20)
.withWatch(); // Ensures that we are watching the channel for any changes/new messages
channelClient.query(request).enqueue(new Call.Callback<Channel>() {
@Override
public void onResult(@NotNull Result<Channel> result) {
if (result.isSuccess()) {
Channel channel = result.data();
// Use channel
} else {
result.error().getCause().printStackTrace();
}
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
/// 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 = chatClient.channelController(for: channelId)
/// 3: Call `ChatChannelController.synchronize` to create the channel.
channelController.synchronize { error in
if let error = error {
/// 4: Handle possible errors
print(error)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
val channelClient: ChannelClient = client.channel(
channelType = "messaging",
channelId = "travel"
)
val extraData = mutableMapOf<String, Any>(
"name" to "Awesome channel about traveling"
)
// Watching a channel"s state
val request = QueryChannelRequest()
.withData(extraData)
.withMessages(limit = 20)
.withWatch() // Ensures that we are watching the channel for any changes/new messages
channelClient.query(request).enqueue { result ->
if (result.isSuccess) {
val channel: Channel = result.data()
// Use channel
} else {
result.error().cause?.printStackTrace()
}
}
1
2
3
4
5
6
final channel = client.channel("messaging", id: "travel", extraData: {
"name": "Awesome channel about traveling",
});
// fetch the channel state and subscribe to future updates
final state = await channel.watch();
The first two arguments are the Channel Type and the Channel ID (messaging
and travel
in this case). The Channel ID is optional; if you leave it out, the ID is determined based on the list of members. 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. 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
Copied!Confused about "Messages"?
Let us know how we can improve our documentation:
Now that we have the channel set up, let's send our first chat message:
1
2
3
4
5
6
const text = 'I’m mowing the air Rand, I’m mowing the air.';
const response = await channel.sendMessage({
text,
customField: '123',
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Message message = new Message();
message.setText("I’m mowing the air Rand, I’m mowing the air.");
message.putExtraValue("customField", "123");
channelClient.sendMessage(message).enqueue(new Call.Callback<Message>() {
@Override
public void onResult(@NotNull Result<Message> result) {
if (result.isSuccess()) {
Message message = result.data();
} else {
result.error().getCause().printStackTrace();
}
}
});
1
2
3
4
5
6
7
8
9
10
11
/// 1: Create a `ChannelId` that represents the channel you want to create.
channelController.createNewMessage(text: "Hello") { result in
switch result {
case .success(let messageId):
/// 2: Handle success
print(messageId)
case .failure(let error):
/// 3: Handle errors
print(error)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
val message = Message(text = "I’m mowing the air Rand, I’m mowing the air.")
message.extraData["customField"] = "123"
channelClient.sendMessage(message).enqueue(object : Call.Callback<Message> {
override fun onResult(result: Result<Message>) {
if (result.isSuccess) {
val message = result.data()
} else {
result.error().cause?.printStackTrace()
}
}
})
1
2
3
4
5
6
final message = Message(
text: 'I’m mowing the air Rand, I’m mowing the air.',
extraData: {'customField': '123'},
);
final response = await channel.sendMessage(message);
Similar to users and channels, the sendMessage
method allows you to add custom fields. When you send a message to a channel, Stream Chat automatically broadcasts to all the people that are watching this channel and updates in real-time.
Events
Copied!Confused about "Events"?
Let us know how we can improve our documentation:
This is how you can listen to events on the clients-side:
1
2
3
4
channel.on('message.new', event => {
console.log('received a new message', event.message.text);
console.log(`Now have ${channel.state.messages.length} stored in local state`);
});
1
2
3
4
5
6
7
8
9
final Disposable disposable = client.subscribe((event) -> {
if (event instanceof NewMessageEvent) {
Message message = ((NewMessageEvent) event).getMessage();
}
return Unit.INSTANCE;
});
// Dispose to stop receiving events
disposable.dispose();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//> import UIKit
//> import Combine
//> import StreamChat
/// * Delegates *
class ChannelViewController: ChatChannelControllerDelegate {
func channelController(_ channelController: ChatChannelController, didUpdateMessages changes: [ListChange<ChatMessage>]) {
// animate the changes to the message list
}
}
let channelViewController = ChannelViewController()
channelController.delegate = channelViewController
/// * Combine *
channelController.messagesChangesPublisher
.receive(on: RunLoop.main)
.sink { changes in
// animate the changes to the message list
}
.store(in: &cancellables)
1
2
3
4
5
6
7
val disposable = client.subscribe { event: ChatEvent ->
if (event is NewMessageEvent) {
val message = event.message
}
}
// Dispose to stop receiving events
disposable.dispose()
1
2
3
channel.on("message.new").listen((Event event) {
print("received a new message: ${event.message.text}");
};
channel.state
.Conclusion
Copied!Confused about "Conclusion"?
Let us know how we can improve our documentation:
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 API endpoint.