// Typically done in your Application class using your API Key on startup
val client = ChatClient.Builder("{{ api_key }}", context).build()
// Static reference to initialised client
val staticClientRef = ChatClient.instance()Initialization & Users
The code below creates a chat client instance for interacting with Stream APIs. A singleton client instance means the Chat client is created once and reused throughout your app, ensuring consistent state, avoiding duplicate connections, and simplifying resource management.
import { StreamChat } from "stream-chat";
// client-side you initialize the Chat client with your API key
const chatClient = StreamChat.getInstance("{{ api_key }}", {
timeout: 6000,
});// API key can be found on the dashboard: https://getstream.io/dashboard/
let config = ChatClientConfig(apiKey: .init("<# Your API Key Here #>"))
// Create an instance of ChatClient
let chatClient = ChatClient(config: config)
// Recommendation is to store it as a shared instance
extension ChatClient {
static var shared: ChatClient!
}
ChatClient.shared = chatClient// client-side you initialize the Chat client with your API key
final client = StreamChatClient(
"{{ api_key }}",
logLevel: Level.INFO,
connectTimeout: Duration(milliseconds: 6000),
receiveTimeout: Duration(milliseconds: 6000),
);Client = CreateDefaultSubobject<UStreamChatClientComponent>(TEXT("Client"));
Client->ApiKey = TEXT("{{ api_key }}");// Typically done in your Application class on startup
ChatClient client = new ChatClient.Builder("{{ api_key }}", context).build();
// Client singleton is also available via static reference
ChatClient staticClientRef = ChatClient.instance();var client = StreamChatClient.CreateDefaultClient();Connecting Users
Once the client is initialized, your app authenticates the user and establishes a Websocket connection by calling connectUser. This function uses your token provider function to request a token from your server.
The connectUser function acts as an upsert for the user object and is a primary method for creating users client-side.
Before attempting subsequent API requests to Stream, it is important that the connectUser function fully resolves.
val user = User(
id = "bender",
extraData = mutableMapOf(
"name" to "Bender",
"image" to "https://bit.ly/321RmWb",
),
)
// You can setup a user token in two ways:
// 1. Setup the current user with a JWT token
val token = "{{ chat_user_token }}"
client.connectUser(user, token).enqueue { result ->
if (result is Result.Success) {
// Logged in
val user: User = result.value.user
val connectionId: String = result.value.connectionId
} else {
// Handle Result.Failure
}
}
// 2. Setup the current user with a TokenProvider
val tokenProvider = object : TokenProvider {
// Make a request to your backend to generate a valid token for the user
override fun loadToken(): String = yourTokenService.getToken(user)
}
client.connectUser(user, tokenProvider).enqueue { /* ... */ }await chatClient.connectUser(
{
id: "john",
name: "John Doe",
image: "https://getstream.io/random_svg/?name=John",
},
tokenProvider,
);// 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/tokens_and_authentication/?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 method
let connectedUser = try await ChatClient.shared.connectUser(
userInfo: .init(id: userID),
token: token
)final user = User(id: "john", extraData: {
"name": "John Doe",
"image": "https://i.imgur.com/fR9Jz14.png",
});
await client.connectUser(user, "{{ chat_user_token }}");const FUser User{TEXT("john")};
const FString Token{TEXT("{{ chat_user_token }}")};
Client->ConnectUser(
User,
Token,
[](const FOwnUser& UserRef)
{
// Connection established
});User user = new User();
user.setId("bender");
user.setName("Bender");
user.setImage("https://bit.ly/321RmWb");
// You can setup a user token in two ways:
// 1. Setup the current user with a JWT token
String token = "{{ chat_user_token }}";
client.connectUser(user, token).enqueue(result -> {
if (result.isSuccess()) {
// Logged in
User userRes = result.data().getUser();
String connectionId = result.data().getConnectionId();
} else {
// Handle result.error()
}
});
// 2. Setup the current user with a TokenProvider
TokenProvider tokenProvider = new TokenProvider() {
@NotNull
@Override
public String loadToken() {
return yourTokenService.getToken(user);
}
};
client.connectUser(user, tokenProvider).enqueue(result -> {/* ... */});var localUserData = await client.ConnectUserAsync("api_key", "chat_user", "chat_user_token");
// After await is complete the user is connected
// Alternatively, you subscribe to the IStreamChatClient.Connected event
client.Connected += localUserData =>
{
// User is connected
};Connect User Parameters
| name | type | description | default | optional |
|---|---|---|---|---|
| user | object | The user object. Must have an id field. User Ids can only contain characters a-z, 0-9, and special characters @ _ and - It can have as many custom fields as you want, as long as the total size of the object is less than 5KB | ||
| userToken | string/function | The Token Provider function or authentication token. See Tokens & Authentication for details | default |
Disconnecting Users
The client-side SDKs handle WebSocket disconnection logic, but if a manual disconnect is required in your application, there are the following options:
ChatClient.instance().disconnect(flushPersistence = false).enqueue { /* ... */ }await chatClient.disconnectUser();chatClient.disconnect {
// disconnected
}
// or
await chatClient.disconnect()await client.disconnectUser();Client->DisconnectUser();ChatClient.instance().disconnect(true).enqueue();await client.DisconnectUserAsync();XHR Fallback
Most browsers support WebSocket connections as an efficient mode of real-time data transfer. However, sometimes the connection cannot be established due to network or a corporate firewall. In such cases, the client will establish or switch to XHR fallback mechanisms and gently poll our service to keep the client up-to-date.
The fallback mechanism can be enabled with the flag enableWSFallback
const chatClient = StreamChat.getInstance(‘apiKey’, { enableWSFallBack: true });Privacy Settings
Additionally, when connecting the user, you can include the privacy_settings as part of the user object.
val user = User(
id = "bender",
extraData = mutableMapOf(
"name" to "Bender",
"image" to "https://bit.ly/321RmWb",
),
privacySettings = PrivacySettings(
typingIndicators = TypingIndicators(
enabled = false,
),
readReceipts = ReadReceipts(
enabled = false,
)
)
)
client.connectUser(user, token).enqueue { /* ... */ }await chatClient.connectUser(
{
id: "john",
name: "John Doe",
image: "https://getstream.io/random_svg/?name=John",
privacy_settings: {
typing_indicators: {
enabled: false,
},
read_receipts: {
enabled: false,
},
},
},
"{{ chat_user_token }}",
);// Connect the user using a closure based method
chatClient.connectUser(
userInfo: .init(
id: userID,
privacySettings: .init(
typingIndicators: .init(enabled: true),
readReceipts: .init(enabled: true)
)
),
token: token
) { error in
// …
}
// or alternatively, using the async-await method
let connectedUser = try await chatClient.connectUser(
userInfo: .init(
id: userID,
privacySettings: .init(
typingIndicators: .init(enabled: true),
readReceipts: .init(enabled: true)
)
),
token: token
)final user = OwnUser(
id: "john",
extraData: {
"name": "John Doe",
"image": "https://i.imgur.com/fR9Jz14.png",
},
privacySettings: PrivacySettings(
typingIndicators: TypingIndicators(
enabled: false,
),
readReceipts: ReadReceipts(
enabled: false,
),
),
);
await client.connectUser(user, "{{ chat_user_token }}");| name | type | description | default | optional |
|---|---|---|---|---|
| typing_indicators | object | if enabled is set to false, then typing.start and typing.stop events will be ignored for this user and these events will not be sent to others | enabled: true | ✓ |
| read_receipts | object | If enabled is set to false, then the read_state of this user will not be exposed to others. Additionally, read_state related events will not be delivered to others when this user reads messages. | enabled: true | ✓ |