Getting Started

The SwiftUI SDK is built on top of the StreamChat framework, and it’s a SwiftUI alternative to the StreamChatUI SDK. It’s made entirely in SwiftUI, using declarative patterns that will be familiar to developers working with SwiftUI.

This section provides a high-level overview of the SwiftUI components library. It is a great starting point for discovering how to use Stream’s SwiftUI components in your app. For a complete step-by-step guide, check out iOS Chat tutorial.

Before starting this guide, make sure you have installed StreamChatSwiftUI as explained in the Installation section.

Setup

The SwiftUI SDK provides a context provider object that allows simple access to functionalities exposed by the SDK, such as branding, presentation logic, icons and the low-level ChatClient. The first step you would need to start using the SDK is to create the context provider object, called StreamChat.

let apiKeyString = "your_api_key_string" let config = ChatClientConfig(apiKey: .init(apiKeyString)) let client = ChatClient(config: config) let streamChat = StreamChat(chatClient: chatClient)

It would be best to do this setup when the app starts, for example, in the AppDelegate’s didFinishLaunchingWithOptions method. Also, be sure to keep a strong reference to the created StreamChat instance, for example, as a variable in the AppDelegate. This is the minimal setup that is required for the context provider object.

Connect User

The next step is to connect the ChatClient with a user. In order to connect, the chat client needs an authorization token.

In case the token does not expire, the connection step can look as follows:

// You can generate the token for this user from /chat/docs/ios-swift/token_generator/ // make sure to use the `leia_organa` as user id and the correct API Key Secret. let nonExpiringToken: Token = "<# User Token Here #>"  // The user details. let userInfo = UserInfo(  id: "leia_organa",  name: "Leia Organa",  imageURL: URL(string: "https://cutt.ly/SmeFRfC") )  // Connect the client with the static token ChatClient.shared.connectUser(userInfo: userInfo, token: nonExpiringToken) { error in  /* handle the connection error */ }

This example has the user and its token hard-coded. But the best practice is to fetch the user and generate a valid chat token on your backend infrastructure.

In case of a token with an expiration date, the chat client should be connected by giving the token provider that is invoked for initial connection and also to obtain the new token when the current token expires:

// The user details. let userInfo = UserInfo(  id: "leia_organa",  name: "Leia Organa",  imageURL: URL(string: "https://cutt.ly/SmeFRfC") )  // Create a token provider that uses the backend to retrieve a new token. let tokenProvider: TokenProvider = { completion in  yourAuthService.fetchToken(for: userInfo.id, completion: completion) }  // Connect the client with the token provider. ChatClient.shared.connectUser(userInfo: userInfo, tokenProvider: tokenProvider) { error in  /* handle the connection error */ }

Disconnect & Logout

Whenever your users leave the chat component, you should use disconnect to stop receiving chat updates and events while using other features of your app. You disconnect by calling:

chatClient.disconnect {  // Dismiss the current screen or go to another screen.  print("disconnect completed") }

If your users logout from their account you should use logout instead of disconnect. Logout disconnects and deletes the offline state for the current user. You logout by calling:

chatClient.logout {  // Dismiss the current screen or go to another screen  print("logout completed")  }

It’s important that you wait for the execution to complete before trying to login with a different user.

Create a Channel

Lets create your first channel so that we can later display it in the channel list view.

do {  let channelId = ChannelId(type: .messaging, id: UUID().uuidString)  let channelController = try ChatClient.shared.channelController(  createChannelWithId: channelId,  name: "My First Channel"  )  channelController.synchronize { error in  if let error = error {  print(error)  }  } } catch {  print("Channel creation failed because the current user is not connected") }

The channel type is an enum that describes what the channel’s intention is.

Your ChannelId has to be a unique ID and you can set this to anything, in this example we’re using the UUID() provided by Apple. Finally, you can pass through the name of the channel which is a String and also some additional parameters if required.

After creating the channel it’s important you call synchronize() or get(watch: true), depending if you are using completion handler or async/await versions. Internally, this will fetch the local data first, then the remote data and it will keep it up-to-date by observing the web socket events.

Show the Channel List

Now that we have a channel, we can show it in the Channel List.

Here is an example of how to present the Channel List in SwiftUI:

struct ContentView: View {  @Injected(\.chatClient) var chatClient   var body: some View {  ChatChannelListView(  channelListController: channelListController  )  }  let streamChat = StreamChat(chatClient: chatClient, appearance: appearance)

In this way, you can change all the colors used in the SDK. For a complete reference, please check our source code here.

Changing Images

All of the images used in the SDK can be replaced with your custom ones. To customize the images, create a new instance of the Images class and update the images you want to change. For example, if we’re going to change the love reaction, we need to override the corresponding image property.

let images = Images() images.reactionLoveBig = UIImage(systemName: "heart.fill")!  let appearance = Appearance(colors: colors, images: images)  let streamChat = StreamChat(chatClient: chatClient, appearance: appearance)

The full reference of images can be found here.

Changing Fonts

You can provide your font to match the style of the rest of your app. In the SDK, the default system font is used, with dynamic type support. To keep this support with your custom fonts, please follow Apple’s guidelines about scaling fonts automatically.

The fonts used in the SDK can be customized via the Fonts struct, which is part of the Appearance class. So, for example, if we don’t want to use the bold footnote font, we can easily override it with our non-bold version.

var fonts = Fonts() fonts.footnoteBold = Font.footnote  let appearance = Appearance(colors: colors, fonts: fonts)  let streamChat = StreamChat(chatClient: chatClient, appearance: appearance)

Similarly, you can create your own font and replace the corresponding property. The full reference of fonts can be found here.

Changing Presentation Logic

In some cases, you might want to change parts of the way the data is displayed in our UI components. For example, you want to change the date formatting, the naming logic of the channels, or use your CDN for storing the images. For cases like this, you should use the Utils class, which is part of the StreamChat object. For example, if we want to change the way the channel names are displayed in the channel list component, we need to do the following:

let channelNamer: ChatChannelNamer = { channel, currentUserId in  "This is our custom name: \(channel.name ?? "no name")" } let utils = Utils(channelNamer: channelNamer)  let streamChat = StreamChat(chatClient: chatClient, appearance: appearance, utils: utils)

Accessing Chat Context Functionalities Through Injectable Variables

If you build your own view components and you want to use the chat context providing options, you can do so in a way that’s very similar to SwiftUI’s environment. You need to define the corresponding keypath of the functionality you need anywhere in your code.

@Injected(\.chatClient) var chatClient @Injected(\.fonts) var fonts @Injected(\.colors) var colors @Injected(\.images) var images @Injected(\.utils) var utils

You can find more details about the dependency injection approach we are using here.

Connect User

The next step is to connect the ChatClient with a user. In order to connect, the chat client needs an authorization token.

In case the token does not expire, the connection step can look as follows:

// You can generate the token for this user from /chat/docs/ios-swift/tokens_and_authentication/ // make sure to use the `leia_organa` as user id and the correct API Key Secret. let nonExpiringToken: Token = "<# User Token Here #>"  // Create the user info to connect with let userInfo = UserInfo(  id: "leia_organa",  name: "Leia Organa",  imageURL: URL(string: "https://cutt.ly/SmeFRfC") )  // Connect the client with the static token chatClient.connectUser(userInfo: userInfo, token: nonExpiringToken) { error in  /* handle the connection error */ }

The code snippet above will also create the ChatChannelListController with the specified query. The ChannelListQuery allows us to define the channels to fetch and their order. In this case, the query will load all the channels the user is a member of.

Read more about channel list query and low-level channel list controller here.

You can load test data for your application using the test data generator here.