How to Implement Chat in Your E-Commerce iOS App

Matheus C.
Matheus C.
Published July 28, 2020 Updated September 21, 2021

In this tutorial, we'll integrate chat in an e-commerce app using Stream Chat's Swift SDK. It's crucial for buyers that may need immediate communication for payments, delivery status, last-minute changes, and refunds.

Screenshot shows an e-commerce iOS app screen with a chat button that leads to the chat screen, also pictured

Those screenshots show a button leading to a chat screen where the buyer can get information in real time from the seller. This is what we'll build.

If you get lost during this tutorial, you can check the completed project in this GitHub repo.

What is Stream Chat?

Build real-time chat in less time. Rapidly ship in-app messaging with our highly reliable chat infrastructure. Drive in-app conversion, engagement, and retention with the Stream Chat messaging platform API & SDKs.

Set up the Stream Chat dependency

To install the Stream Chat dependency, we'll use CocoaPods. If you prefer Carthage or Swift Package Manager, they're also supported.

In your project's folder, if you aren't already using CocoaPods, run pod init and add StreamChat to the Podfile. It should look similar to this:

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'ECommerceApp' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  # Pods for ECommerceApp
  pod 'StreamChat', '~> 2.2'
end

After you do that, run pod install, wait a bit for it to finish, and open the project via the .xcworkspace that was created.

Configure the Stream Chat dashboard

Sign up at GetStream.io, create the application, and make sure to select development instead of production.

Screenshot of a user creating a development application at GetStream.io

To make things simple for now, let's disable both auth checks and permission checks. Make sure to hit save. When your app is in production, you should keep these enabled.

Screenshot of skip auth checks and permission being enabled in a Stream App dashboard

You can see the documentation about authentication here and permissions here.

Now, save your Stream credentials, as we'll need them to power the chat in the app in the next step. Since we disabled auth and permissions, we'll only really need the key for now, but in production, you'll use the secret in your backend to implement proper authentication to issue user tokens for Stream Chat, so users can interact with your app securely.

Screenshot of credentials on stream dashboard

As you can see, I've blacked out my keys. You should make sure to keep your credentials safe.

Configure Stream Chat SDK

Now that we've set up the project and Stream Chat dashboard let's configure the chat SDK's singleton with the Stream Chat App's key you got in the last step. The didFinishLaunchingWithOptions function in AppDelegate.swift should look similar to the following snippet.

import UIKit
import StreamChatClient

...
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        Client.configureShared(.init(apiKey: "31e2eqp53qu3", logOptions: .info))
        return true
    }
...

That will configure the Client.shared instance, which will be used by the Stream Chat UI components to make calls to the Stream Chat API and subscribe to events.

Create the Join Screen

Let's start building the "Join" screen. This screen consists of two UIButton instances. One to join as a Buyer, and the other to join as a Seller. This is, of course, an oversimplification to make this tutorial short and get to the chat features faster. In your complete app, you'll need proper registration, database, and all that. It's also likely that you'll want a separate app for the sellers. For this tutorial, the screen will look similar to the screenshot below.

Screenshot shows an app with two buttons, one to join as a buyer, and the other to join as the seller

Go to the storyboard, select the default view controller, and click Editor > Embed In > Navigation Controller. That will place it under a navigation controller, which we'll use to navigate to the next screen.

Image shows storyboard with a JoinViewController embedded in a navigation controller

Make sure to rename ViewController to JoinViewController, so you don't get confused later on. You can do this easily by right-clicking on ViewController in ViewController.swift and selecting refactor.

To make things simple, let's leave the storyboard like this and use only code from now on. To set up the two buttons, we need the following code in JoinViewController.swift:

import UIKit

class JoinViewController: UIViewController {
    let buyerButton = UIButton()
    let sellerButton = UIButton()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        title = "Join"
        
        setupViews()
        setupConstraints()
        setupHandlers()
    }
}

That code sets up the views, the constraints, and the handlers we need. Let's start by extending JoinViewController to define setupViews:

extension JoinViewController {
    func setupViews() {
        setupBuyerButton()
        setupSellerButton()
    }
    
    private func setupBuyerButton() {
        buyerButton.translatesAutoresizingMaskIntoConstraints = false
        buyerButton.setTitleColor(.systemBlue, for: .normal)
        buyerButton.setTitle("Buyer 🙋", for: .normal)
        buyerButton.titleLabel?.font = .systemFont(ofSize: 32)
        
        view.addSubview(buyerButton)
    }
    
    private func setupSellerButton() {
        sellerButton.translatesAutoresizingMaskIntoConstraints = false
        sellerButton.setTitleColor(.systemBlue, for: .normal)
        sellerButton.setTitle("Seller 🧑‍💼", for: .normal)
        sellerButton.titleLabel?.font = .systemFont(ofSize: 32)
        
        view.addSubview(sellerButton)
    }
}

That code will create the buttons and add them to the controller's view. Next, we need to define constraints between the three. Let's do this by extending JoinViewController to define setupConstraints:

extension JoinViewController {
    func setupConstraints() {
        view.addConstraints([
            buyerButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            buyerButton.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor, constant: -100),
            sellerButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            sellerButton.centerYAnchor.constraint(equalTo: buyerButton.centerYAnchor, constant: 100)
        ])
    }
}

That code will make sure the buyerButton stays in the center of the screen and the sellerButton below it. Now we need to set up the handlers for when the user presses the buttons. Let's do this again by extending the controller to define setupHandlers:

import StreamChat
import StreamChatClient

extension JoinViewController {
    func setupHandlers() {
        buyerButton.addTarget(self, action: #selector(handleBuyerButtonPress), for: .touchUpInside)
        sellerButton.addTarget(self, action: #selector(handleSellerButtonPress), for: .touchUpInside)
    }
    
    @objc private func handleBuyerButtonPress() {
        Client.shared.set(user: .init(id: .random()), token: .development) { result in
            switch result {
            case .success:
                let buyerVC = BuyerViewController()
                self.navigationController?.pushViewController(buyerVC, animated: true)
            case .failure(let error):
                print(error)
            }
        }
    }
    
    @objc private func handleSellerButtonPress() {
        Client.shared.set(user: .init(id: "Seller"), token: .development) { result in
            switch result {
            case .success:
                let channelsVC = ChannelsViewController()
                channelsVC.title = "Messages"
                channelsVC.presenter = .init(filter: .equal("type", to: "messaging"))
                self.navigationController?.pushViewController(channelsVC, animated: true)
            case .failure(let error):
                print(error)
            }
        }
    }
}

That code will make it so when the buyer button is pressed, a BuyerViewController is presented, and when the seller button is pressed a ChannelsViewController, which is provided by Stream Chat, is presented for a seller to see the open support channels. Additionally, we're generating a random id for the buyer using the String extension below.

import Foundation

extension String {
    static func random(length: Int = 10) -> String {
        let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        return String((0..<length).map{ _ in letters.randomElement()! })
    }
}

In a real-world app this is normally defined in the registration and login processes.

We'll create BuyerViewController in the next step.

Create the Buyer Screen

Now, let's create the screen that represents our regular app and through which a regular buyer can access the support chat. There are many ways different apps use to access their support system, such as floating buttons and menu items. In this tutorial we'll introduce a button in the navigation bar for opening the support chat. It will look similar to the screenshot below.

Screenshot shows an app window with a support button that leads to a chat screen with the seller

The first step is to create a BuyerViewController.swift file and paste the code below.

import UIKit

class BuyerViewController: UIViewController {
    let chatButton = UIBarButtonItem()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupViews()
        setupHandlers()
    }
}

That code sets up the views and the handlers we need. Let's start by extending JoinViewController to define setupViews:

import UIKit

extension BuyerViewController {
    func setupViews() {
        view.backgroundColor = .systemBackground
        setupChatButton()
    }
    
    func setupChatButton() {
        chatButton.image = UIImage(systemName: "exclamationmark.bubble")
        navigationItem.rightBarButtonItem = chatButton
    }
}

That code will place the support button at the top right in the navigation bar. However, we still need to define the handler for that button press. Let's do that by defining setupHandlers:

import StreamChat
import StreamChatClient

extension BuyerViewController {
    func setupHandlers() {
        chatButton.target = self
        chatButton.action = #selector(handleChatButtonPress)
    }
    
    @objc func handleChatButtonPress() {
        let uid = Client.shared.user.id
        let channel = Client.shared.channel(type: .messaging, id: "support-\(uid)")
        channel.extraData = ChannelExtraData(name: "\(uid) support")
        channel.create { _ in
            channel.add(user: .init(id: "Seller")) { _ in
                
            }
        }
        
        let chatVC = ChatViewController()
        chatVC.presenter = .init(channel: channel)
        chatVC.title = "Messages"
        
        let navigation = UINavigationController(rootViewController: chatVC)
        
        self.present(navigation, animated: true, completion: {
            
        })
    }
}

That code will create a messaging channel and present a ChatViewController, provided by Stream Chat, that will have all the behavior we need for the chat.

Screenshot shows a fully featured chat screen with the seller

If you're a seller, you'll be able to see all channels created by buyers and access the chat screen for each of them.

Screenshots show a channels list and a chat screen

E-commerce chat completed

Congratulations! You've implemented the basis of a functioning chat system for your e-commerce app with Stream Chat. I encourage you to browse through Stream Chat's docs and experiment with the project you just built.

Integrating Video With Your App?
We've built a Video and Audio solution just for you. Check out our APIs and SDKs.
Learn more ->