Build a Chat App Using Stream and SwiftUI

Chase
5 min readApr 29, 2024

Building a chat app from scratch has never been this easy or this quick. Stream has supported a few different conferences lately and I wanted to give them a try. Below you will find out just how easy it is and how little code is actually required to get a fully functioning chat app up and running.

A screenshot of the app we will build
A screenshot of the app we will build in less than 100 lines of code

Before we get started, please take a couple of seconds to follow me and 👏 clap for the article so that we can help more people learn about this useful content.

Jumping right in

To get setup with Stream we will want to add their SwiftUI package using the Swift Package Manager. You can use the following link to find their SwiftUI package in SPM: https://github.com/getstream/stream-chat-swiftui(side note, there GitHub page currently says that they are hiring, so if you are interested, feel free to check them out). Fair warning, downloading the package usually takes a little while. Once the package has been downloaded be sure to your app is added in “Add To Target” section since their repo contains most of the UI we will need and we want to make sure it gets added to our project when we build and/or bundle our app.

A screenshot of us adding the Stream Chat SwiftUI repo to our app as the Target

Even though we won’t be going into detail on how to get an auth token from an auth provider or your own server, I still wanted to call out that step explicitly since it would be required in a production app. If you are wondering how it works, there are many slight variants on how exactly getting an auth token can occur, but the basics are that we would make an asynchronous request to the auth provider and assuming the user and credentials were valid, then a valid token would be returned. We will pick back up at the point where a valid token would be returned by simply hard coding the token for now (since this is a chat tutorial and not an auth tutorial). We are also configuring our chatClient in the same file (for the sake of simplicity), however, the API token is also something that you would likely return from your authentication endpoint.

Going over the view body, we have a group with an onAppear modifier that is allowing us to sign our hard coded user in to the service that Stream provides. If the connectUser method was successful, it sets the boolean to display the ChatChannelListView. This view is one that we get for free as a built in component from Stream and already has navigation built in so that when you select a chat thread that you are interested in, that thread gets pushed onto the view. If there was a failure when trying to sign the user into the service, the ContentUnavailableView is the one that gets displayed. Even though this is a demo app, it’s always a good idea to code defensively (knowing where something could fail, like a sign in process), if nothing else, it helps you form good habits. The last piece of code in this view is the one that actually connects our user to the service. I went ahead and wrapped the connect function and decided to pass in the parameters that we would need so that this function to could more easily extracted out to another file if you decide that would work best in your project. With just that small amount of code, we have a working chat app (see screenshots above).

//  ContentView.swift
import SwiftUI
import StreamChat
import StreamChatSwiftUI

struct ContentView: View {
@State var streamChat: StreamChat?
@State var userIsLoggedIn = false

// User Info for a Demo User from the Stream demo app
// https://github.com/GetStream/stream-chat-swiftui/blob/d4261afc91033ba252e14f8b839d7c8698e3de65/DemoAppSwiftUI/DemoUser.swift
let id = "luke_skywalker"
// This token is in JWT format,
// you can copy and paste it into https://jwt.io to see what's encoded in it
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoibHVrZV9za3l3YWxrZXIifQ.b6EiC8dq2AHk0JPfI-6PN-AM9TVzt8JV-qB1N9kchlI"

var chatClient: ChatClient = {
// In production code, this key is specific to your app
// however, in this tutorial the key also comes from the Stream demo app linked above
let config = ChatClientConfig(apiKeyString: "zcgvnykxsfm8")
let client = ChatClient(config: config)
return client
}()

var body: some View {
Group {
if userIsLoggedIn {
ChatChannelListView()
} else {
ContentUnavailableView(
"There was an error logging in the user",
systemImage: "person.crop.circle.badge.exclamationmark",
description: Text("Check the logs, the credentials for the current user, or try re-running the app")
)
}
}
.onAppear {
connectUser(id: id, token: token, chatClient: chatClient)
}
}

private func connectUser(id: String, token: String, chatClient: ChatClient) {
// make sure the user is logged out before we try to log another one in again
userIsLoggedIn = false
chatClient.logout {}

guard let token = try? Token(rawValue: token) else {
log.error("ERROR: Creating Token from value")
return
}

// initalize the StreamChat interface
streamChat = StreamChat(chatClient: chatClient)

// connect the user to the chatClient
chatClient.connectUser(userInfo: UserInfo(id: id), token: token) { error in
if let error = error {
log.error("ERROR: Connecting the user failed \(error.localizedDescription)")
return
}
}

userIsLoggedIn = true
}
}

#Preview {
ContentView()
}

If you want to include photos from the users library or allow the user to take images or record audio into the messages that the users will send (all of which you also get for free thanks to the built in components from Stream), you will want to make sure that you add those permissions to your info.plist file (as seen in the screenshot below).

A screenshot of the options that you will need to manually add to your info.plist file
These will need to be manually added to your info.plist file

If you got value from this article, please consider following me, 👏 clapping for this article, or sharing it to help others more easily find it.

If you have any questions on the topic or know of another way to accomplish the same task, feel free to respond to the post or share it with a friend and get their opinion on it. If you want to learn more about native mobile development, you can check out the other articles I have written here: https://medium.com/@jpmtech. If you want to see apps that have been built with native mobile development, you can check out my apps here: https://jpmtech.io/apps. Thank you for taking the time to check out my work!

--

--