How to Build a Chat App with SwiftUI: Part 2 (Channels)

In Part 1 of this series, we created a simple chat application for iOS and macOS using SwiftUI and Stream Chat's Swift SDK, but it only had a single channel. In this tutorial, we'll improve on it by implementing a channels screen with three features: join, create, and search channels. Although Stream Chat provides a suite of UIKit components, including those for channels, we'll use the low-level client to develop custom components with SwiftUI.

If you get lost during this tutorial, or want to get the completed code for Part 1, you can check the completed project in this GitHub repo. Branch part1 has the code for Part 1.

What you need

  • iOS 13+
  • Xcode 11+
  • A Stream account
  • Followed Part 1.

Step 1: Update the Chat View

In the first part, we developed a Chat View that only displayed the "general" channel. Now we need to make it flexible to display any channel. Your ChatView.swift should look like this:

Instead of the hard-coded "general" channel, that code defines an initializer for ChatView that takes an id and initializes the channel object with that id. Additionally, you'll want to replace the hard-coded snippet .navigationBarTitle("General") with .navigationBarTitle(, so it displays the title correctly.

Before proceeding to the next step, let's also add Client.shared.add(users: [Client.shared.user], to: channel, { _ in }) to the end of onAppear on ChatView, so we make sure every channel visited will show up in the channels view.

Step 2: Create the Channels View

Now, we need a Channels View with the channels the user is part of, which will look similar to the following screenshot.

Let's create a ChannelsView.swift file and paste the following contents. We'll leave the create channel and search functions for the next steps.

In that snippet of code, we create a List where each item is a NavigationLink to the ChatView. The onAppear callback makes a query to the Stream service for the channels the current user is a member of, or, in case the searchTerm is not empty, we query all channels and filter out the ones that don't contain the searched string in the id. [docs]

Note we're not handling the possible errors that can result from the query. In production, it would be best if you dealt with the possible errors.

Step 3: Modify the Login View

This is a quick step: In Part 1, the Chat View was shown right after login. What we want now is to show the Channels View.

Just swap the destination where it says NavigationLink(destination: ChatView(), tag: true, selection: $success) { with ChannelsView().

Step 4: Implement channel creation

After that small tweak to the Login View, let's implement the Create Channel function. First, go back to ChannelsView.swift and uncomment the //createChannelView in the body.

Now, we need to add the following property and methods.

When the user presses the create button on the top right, it will set the createTrigger, which activates the display of the createChannelView. This view is a horizontal stack of a TextField and a Button, which, when pressed, runs the createChannel function. That function takes the contents of the text field and creates a channel with that as the id, after that it adds the current user to the channel.

Again, to keep the tutorial short, we're not presenting the possible errors, but in production, you should display some message.

In Step 2, we already laid the grounds for the search to happen by specifying a different filter when searchTerm is not empty. Now we only need to build the UI, which will look similar to the screenshots below.

First, go to ChannelsView.swift and uncomment //searchView. Next, define the following property and function.

That code defines a horizontal stack of a TextField and a Button. When the user types something in the text field, the loadChannels function is triggered, showing the channels that contain the search term in their ids. At the same time, the clear button is displayed. When pressed, it resets the view back to the original state.

Wrapping up

Congratulations! You've built a channels screen and learned a few functions to interact with the Stream API. Let's recap those: queryChannels to display the list of channels, channel.create to create a channel, and add(users: [], to: channel) to add the user to a channel. Since this is the last part of this series, I encourage you to browse through SwiftUI's docs, Stream Chat's docs, and experiment with the project you just built.