iOS Chat With The Stream Swift SDK

Learn how to use our iOS Swift Chat SDK in this comprehensive step-by-step tutorial.

Ready to get started? We are going to over everything you need to build an iPhone chat app with the Stream iOS SDK written in Swift. At the end of the tutorial, you will have created a fully functional Chat application that you can continue to build on.

Looking for more? We also recently published articles on Stream Chat Push Notifications, Comparing Chat API Pricing; and for feeds please check out our React Native Activity Feeds which details using our Activity Feed React Components.

iOS Chat SDK Setup

To get started with the iOS Swift Chat SDK, create a new Swift project in Xcode 11 with a “Single View App” template using the project name ChatDemo.

Select Storyboard in the User Interface options.

Close the Xcode project for now. We will use Cocoapods dependency manager to setup Stream Chat, which will generate Xcode workspace file for our project.

Open a terminal and navigate to your project path:

cd ~/path/to/my/projects/ChatDemo/


For this tutorial we are going to use CocoaPods as dependency manager.

You can also install the SDK with Cartage or with Swift Package Manager

If you do not currently have CocoaPods installed, you can do so by running the following command:

sudo gem install cocoapods

Please note that if you already have CocoaPods installed, make sure you run the most recent version or you may receive errors later on in the development process.

sudo gem update cocoapods

Now that you are in your project directory and have ensured that CocoaPods is installed, let’s go ahead and install the Swift Chat SDK.

Initialize CocoaPods and create a Podfile:

pod init

Open the generated Podfile and modify the contents of the file with the following snippet:

platform :ios, '11.0'

target 'ChatDemo' do
  pod 'StreamChat', '~> 2.2'

Now that we’ve modified our Podfile, let’s go ahead and install the project dependencies via the terminal with one simple command:

pod install --repo-update

The above command will generate the ChatDemo.xcworkspace file automatically.

With our workspace now containing our Pods project with dependencies, as well as our original project, let’s go ahead and move over to Xcode to complete the process.

Add Stream Chat to your iOS application

Open ChatDemo.xcworkspace in Xcode.

open ChatDemo.xcworkspace

To setup Stream Chat we need to add a couple things in your AppDelegate.swift file. Here is what your AppDelegate.swift file should look like:

import UIKit
import StreamChatClient

class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        Client.configureShared(.init(apiKey: "b67pax5b2wdq", logOptions: .info))
        let userExtraData = UserExtraData(name: "Nameless haze")
        Client.shared.set(user: User(id: "nameless-haze-4", extraData: userExtraData),
                          token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoibmFtZWxlc3MtaGF6ZS00In0.qrn4USgOC4zNDVO6lvdEDJMacTq4i_B-jc83-nQ2zRE")
        return true

Note: You will get a compiler error No such module ‘StreamChat’, it’s expected before building. Build the project using Command+B or ignore the error for now, we’ll build the project before running anyway.

Your app is now configured with the API credentials for the demo app. Because we are in development, we configured the client to be more verbose than usual; on a real application you probably want to only log warning of error messages.

Note that Client.shared is a singleton we define in app delegate and re-use throughout the entire application. The shared client manages current user's session (token) and your Stream application API credentials (apiKey).

Because this is a tutorial, we include pre-generated a user token. In a real-world application, your authentication backend would generate such token at log-in / signup and hand it over to the mobile app. More information about this is available on the Chat API docs.

Message Attachments

In order to attach images or camera pictures to messages, your iOS chat app needs to request access from the user. Once you do that, you will be able to pick files and images and add them to messages.

You have to make a few changes to the Info.plist file.

  • NSPhotoLibraryUsageDescription — will give access to the user photo library,
  • NSCameraUsageDescription — will give access to the camera for taking photos,
  • NSMicrophoneUsageDescription — will give access to the microphone for taking video from the camera.
<string>$(PRODUCT_NAME) would like to access your camera</string>
<string>$(PRODUCT_NAME) would like to access your microphone</string>
<string>$(PRODUCT_NAME) would like to access your photo</string>

Multiple Conversations

Most chat iOS applications handle more than just one single conversation. Facebook Messenger, Whatsapp and Telegram all allow you to have multiple one-to-one and group conversations.

Let’s see how we can change our iOS application so that we first see the list of channels our users belong to.

  1. We want to show all channels that have the current user as a member; the ChannelsViewController supports custom filters. In this case we are going to use .currentUserInMembers. More information about this is available on the docs

    Your SceneDelegate.swift should now look like this:

    import UIKit
    import StreamChatClient
    import StreamChatCore
    import StreamChat
    class SceneDelegate: UIResponder, UIWindowSceneDelegate {
        var window: UIWindow?
        func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
            // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
            // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
            // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
            guard let _ = (scene as? UIWindowScene) else { return }
            if let navigationController = window?.rootViewController as? UINavigationController,
                let channelsViewController = navigationController.viewControllers.first as? ChannelsViewController {
                channelsViewController.presenter = ChannelsPresenter(filter: .currentUserInMembers)
  2. Open Xcode menu Editor > Embed In > Navigation Controller. (Make sure that the View Controller is selected in the storyboard or you won’t be able to)

Now we only need to update the view controller created by Xcode to inherit from ChannelsViewController so that it will show the list of channels

Open ViewController.swift and change it to this:

import UIKit
import StreamChatClient
import StreamChatCore
import StreamChat

/// Use ChannelsViewController as the parent view controller.
class ViewController: ChannelsViewController {

That’s it! If you run the iOS application it will show the list of channels your user has access to. The channel list supports pagination out of the box and keeps channels’ order synchronized automatically.

Not all iOS applications have the same logic when it comes to listing channels. Both the APIs and SDK allow you to provide your own filtering and ordering parameters.

You can find more about how the list of channels can be filtered and ordered in the API docs and on the SDK docs.

Chat Features

Did you already try to post a message like this? /giphy awesome

Stream Chat comes with out-of-the-box features such as:

  1. Commands: focus on the composer and type / to use commands, like /giphy
  2. Reactions: tap on a message to add reactions or long press on images
  3. Link preview will be generated automatically when you send a link.
  4. Attach images: use the plus button ⨁ in the composer to attach images or files.
  5. Edit message: long press on your own messages to open the menu with the Edit button.
  6. Typing events: you’ll see typing events at the bottom of messages when someone starts typing.
  7. Threads: you can create threads to any message in a channel, just tap on a message and hit Reply to enter the thread view.

Some of the chat features are hard to see in action with just one user online. If you are interested, you can open the same channel on the web and try user to user interactions like typing events, reactions and threads.

Customize Channel Preview

So far you’ve learned how to use the default components. The library has been designed to be fully extendable, enabling you to build any type of chat. In particular we aim to support these 5 use cases:

  • Social/Messaging chat
  • Team/Slack style chat
  • Customer support chat
  • Livestream chat
  • Gaming

Let’s see how we can make some changes to the SDK’s UI components. We will start by changing how channel previews are shown in the channel list and include the number of unread messages for each.

ChannelsViewController renders each channel preview as a table cell;, all we need to do here is to subclass it and override channelCell(at indexPath: IndexPath, channelPresenter: ChannelPresenter) and include our unread count there.

Open ViewController.swift and change it to this:

import UIKit
import StreamChatClient
import StreamChatCore
import StreamChat

/// Use ChannelsViewController as the parent view controller.
class ViewController: ChannelsViewController {
    /// We override the inherited method for a channel cell.
    /// Here we can create absolutely new table view cell for the channel.
    override func channelCell(at indexPath: IndexPath, channelPresenter: ChannelPresenter) -> UITableViewCell {
        // For now, get the default channel cell.
        let cell = super.channelCell(at: indexPath, channelPresenter: channelPresenter)

        // We need to check if the returned cell is ChannelTableViewCell.
        guard let channelCell = cell as? ChannelTableViewCell else {
            return cell

        let unreadCount = channelPresenter.channel.unreadCount.messages

        // Check the number of unread messages.
        if unreadCount > 0 {
            // Add the info about unread messages to the cell.
            channelCell.update(info: "\(unreadCount) unread", isUnread: true)

        return channelCell

Now we can run the app and see two states of the channel cell:

Press the button below to send yourself a message, you can see how the unread count changes in real-time.

Custom Message

Let’s move to the chat screen and make custom UI for messages. Let’s create another view controller for that:

  • Select ChatDemo folder in the File inspector where we’ll create a new view controller.
  • Select in the File menu > New > File...
  • Choose the Cocoa Touch Class template
  • Type the name of the class as DemoChatViewController
  • Type the subclass as ChatViewController (it’s from Stream Chat SDK)
  • Press next and create the file
  • Change the code of the DemoChatViewController.swift to this:
import UIKit
import StreamChatClient
import StreamChatCore
import StreamChat

class DemoChatViewController: ChatViewController {

    /// Override the default implementation of UI messages
    /// with default UIKit table view cell.
    override func messageCell(at indexPath: IndexPath, message: Message, readUsers: [User]) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "message")
            ?? UITableViewCell(style: .value2, reuseIdentifier: "message")

          cell.textLabel?.text = message.user.name
          cell.textLabel?.font = .systemFont(ofSize: 12, weight: .bold)
          cell.detailTextLabel?.text = message.deleted == nil ? message.text : "This message was deleted"
          cell.detailTextLabel?.font = message.deleted == nil ? .systemFont(ofSize: 12) : .systemFont(ofSize: 12, weight: .light)
          cell.detailTextLabel?.numberOfLines = 0

        return cell

So in the code above:

  1. We override the messageCell method to implement own presentation for messages.
  2. cell.textLabel was updated for a user’s name.
  3. cell.detailTextLabel was updated for a message.

Now that our custom channel view controller we need to get the Channels List view controller to use it, to do this we only need to override the createChatViewController method

Open ViewController.swift and add this new method:

    override func createChatViewController(with channelPresenter: ChannelPresenter) -> ChatViewController {
        return DemoChatViewController()

Run the app, type some messages and we'll see a simple chat with default UIKit table view cell.

Additionally you can add a customization for the loading and status cells. See here.

Note: for complex customizations, the best approach is to implement your message component as a ChannelTableViewCell. You can find more about this in the documentation pages.

Custom Theme

The ChatViewController exposes style properties that you can change to better fit your use-cases. Let’s update the DemoChatViewController class and add some style changes in code:

    required init?(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)

    init() {
      super.init(nibName: nil, bundle: nil)

    func setupStyles() {
        style.incomingMessage.chatBackgroundColor = UIColor(hue: 0.2, saturation: 0.3, brightness: 1, alpha: 1)
        style.incomingMessage.backgroundColor = UIColor(hue: 0.6, saturation: 0.5, brightness: 1, alpha: 1)
        style.incomingMessage.borderWidth = 0
        style.outgoingMessage.chatBackgroundColor = style.incomingMessage.chatBackgroundColor
        style.outgoingMessage.backgroundColor = style.incomingMessage.chatBackgroundColor
        style.outgoingMessage.font = .systemFont(ofSize: 15, weight: .bold)
        style.outgoingMessage.cornerRadius = 0
        style.outgoingMessage.avatarViewStyle = nil

In this case we made styling changes to the ViewController built-in style.

iOS/Swift Chat Tutorial – Final thoughts

In this tutorial we learned how to build a fully functional iOS chat app using Swift. We also showed how easy it is to customize the behavior and build any type of chat or messaging experience.

The underlying chat API is based on Go, RocksDB and Raft. This makes the chat experience extremely fast with response times that are often below 10ms. Stream powers activity feeds and chat for over 500 million end users, so you can feel confident about our APIs.

Both the Swift Chat SDK and the Chat API have plenty more features available to support more advanced use-cases such as push notifications, content moderation, rich messages and more.

Don't forget! We have an Android Chat SDK as well!

Next Steps