# Getting Started

This section provides a high-level overview of the library, core components, and how they fit together. It's a great starting point that you can follow along in your code editor. For a complete, step-by-step guide check our [iOS Chat tutorial](https://getstream.io/tutorials/ios-uikit-chat/).

Before starting this guide, make sure you have installed `StreamChatUI` as explained in the [Installation](/chat/docs/sdk/ios/v4/basics/integration/) section.

## Setup

The first step to use the library is to create an instance of `ChatClient`. It's recommended to instantiate the `ChatClient` as early as possible and ensure that only one `ChatClient` instance is used across your application. For the sake of simplicity, we are going to show this using a singleton pattern:

```swift
extension ChatClient {
    static let shared: ChatClient = {
        // You can grab your API Key from https://getstream.io/dashboard/
        let config = ChatClientConfig(apiKeyString: "<# Your API Key Here #>")

        // Create an instance of the `ChatClient` with the given config
        let client = ChatClient(config: config)

        return client
    }()
}
```

<admonition type="tip">

When using multiple instances of `ChatClient` at the same time, it is required to use a different `ChatClientConfig.localStorageFolderURL` for each instance. For example, adding an additional path component to the default URL.

</admonition>

## 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:

<tabs>

<tabs-item value="swift" label="Swift">

```swift
// 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 */
}
```

</tabs-item>

<tabs-item value="swift-async" label="Swift (Async)">

```swift
// 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
try await ChatClient.shared.connectUser(
    userInfo: userInfo,
    token: nonExpiringToken
)
```

</tabs-item>

</tabs>

<admonition type="warning">

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.

</admonition>

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:

<tabs>

<tabs-item value="swift" label="Swift">

```swift
// 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 */
}
```

</tabs-item>

<tabs-item value="swift-async" label="Swift (Async)">

```swift
// 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.
try await ChatClient.shared.connectUser(
    userInfo: userInfo,
    tokenProvider: tokenProvider
)
```

</tabs-item>

</tabs>


## 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:

<tabs>

<tabs-item value="swift" label="Swift">

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

</tabs-item>

<tabs-item value="swift-async" label="Swift (Async)">

```swift
await chatClient.disconnect()
// Dismiss the current screen or go to another screen.
```

</tabs-item>

</tabs>

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:

<tabs>

<tabs-item value="swift" label="Swift">

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

</tabs-item>

<tabs-item value="swift-async" label="Swift (Async)">

```swift
await chatClient.logout()
// Dismiss the current screen or go to another screen
```

</tabs-item>

</tabs>

<admonition type="warning">

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

</admonition>


## Create a Channel

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

<tabs>

<tabs-item value="swift" label="Swift">

```swift
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")
}
```

</tabs-item>

<tabs-item value="swift-async" label="Swift (Async)">

```swift
let channelId = ChannelId(type: .messaging, id: UUID().uuidString)
let chat = try ChatClient.shared.makeChat(
    with: channelId,
    name: "My First Channel"
)
try await chat.get(watch: true)
```

</tabs-item>

</tabs>

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.

<admonition type="tip">

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.

</admonition>


## 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:

```swift
// 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.shared.connectUser(userInfo: userInfo, token: nonExpiringToken) { error in
 /* handle the connection error */
}
```

<admonition type="note">

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.

</admonition>

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:

```swift
// Create the user info to connect with
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. The token provider is called on `connect` as well as when the current token expires
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:

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

If your users logout form their account you should use logout instead for completely logging out from the session. Logout disconnects and deletes the offline state for the user. You logout by calling:

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

It's important that you wait for the completion handler to be called before trying to login with a different user.

### Show Channel List

Once the `ChatClient` is connected, we can show the list of channels.

To modally show the channel list screen, add the following code-snippet to your app (read more about presentation styles [here](/chat/docs/sdk/ios/v4/uikit/components/channel-list/)):

```swift
guard let currentUserId = ChatClient.shared.currentUserId else { return }
let query = ChannelListQuery(filter: .containMembers(userIds: [currentUserId]))
let channelListController = ChatClient.shared.channelListController(query: query)
let channelListVC = ChatChannelListVC.make(with: channelListController)
let channelListNVC = UINavigationController(rootViewController: channelListVC)
rootViewController.present(channelListNVC)
```

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](/chat/docs/sdk/ios/v4/client/controllers/channels/).

<admonition type="tip">

You can load test data for your application using the test data generator [here](https://generator.getstream.io/).

</admonition>


---

This page was last updated at 2026-04-17T17:33:36.939Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/ios/v4/uikit/getting-started/](https://getstream.io/chat/docs/sdk/ios/v4/uikit/getting-started/).