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.

// 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

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.

// 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
)

Connect User Parameters

nametypedescriptiondefaultoptional
userobjectThe 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
userTokenstring/functionThe Token Provider function or authentication token. See Tokens & Authentication for detailsdefault

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.disconnect {
  // disconnected
}
// or
await chatClient.disconnect()

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.

// 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
)
nametypedescriptiondefaultoptional
typing_indicatorsobjectif 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 othersenabled: true
read_receiptsobjectIf 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