Getting Started - Swift

Project overview

This quick start will take you through the basics of usingChatkit with Swift. At the end of it you will have a working implementation of a 1:1 chat, powered by Chatkit.

You should have some experience with iOS development and Swift. You will also use some Git as well as Node.js for some additional scripting.
You will need the latest version of Xcode installed, as well as Git, Cocoapods, and Node.JS - 8 or above.

You will build an app with a single chatroom with two users in it - Alice and Bob, who can message each other in realtime. You will learn how to interact with the Chatkit API and connect various features together - such as users, messages and the chatroom itself.

The finished app will look like the one in the animation below.

The app you will be building

Preparation

Installing dependencies

To begin, clone the sample repository using Git:github.com/pusher/chatkit-getting-started-swift.
The repository contains two directories - app and server. The first one is the iOS app, and the other contains some scripts you will use to create your chatting environment.

CD into the app directory and install the dependencies by running pod install and opening the newly generated XCWorkspace. The only pod you are installing is PusherChatkit.

Navigating the project

There are 4 files of interest that we have prepared for you:

  • ChatroomController.swift
  • AppDelegate.swift
  • LoginViewController.swift
  • Main.Storyboard

At present, most of them are the default implementations. Looking atMain.Storyboard, you can see how the two screens are related. The user enters the app and is greeted by the Login View Controller. The Login View Controller has a segue called loginWithUserId, that transitions into the Chatroom View Controller - where most of your Chatkit implementation will live.

main storyboard screenshot

The login view has a single text box for entering a user’s ID, and a button for logging in. The chatroom has a table view that will hold our messages in its rows, and a message entry text box and a Send button for sending messages.
Looking at the code, both view controllers have the outlets and actions set for you already, as well as the segue between them.

Now that you have a basic understanding of our project, let’s set up Chatkit. First, you need to create a Chatkit instance.

Creating a Chatkit instance

Head on to dash.pusher.com, and click on the Create button in the Chatkit box. Give your instance a name.

name your instance

Clicking the create button will take you to your instance’s dashboard. Click on the Credentials tab under your instance name, where you’ll see 2 long strings - Instance Locator, and Secret Key.
They are unique for each instance. The instance locator lets your app connect to your Chatkit instance, and you will use the Secret Key on the server to authenticate clients. More on that in a bit.

your credentials

Creating users and rooms

Now that you have created and configured a new Chatkit instance, you will learn how to use a Chatkit server SDK to create some users, a chatroom, and populate it with messages.

In the directory that you cloned from GitHub, there is a serversubdirectory. Use your terminal app to cd back into it.

First, run npm install in that directory to install required dependencies. In this case, that’s just the @pusher/chatkit-serverpackage. This is our server SDK for Node.

Next, open the chatkit.js file in your editor, and replace the valuesYOUR_INSTANCE_LOCATOR and YOUR_SECRET_KEY with the instance locator and secret key values from your instance’s dashboard.
This file configures your Chatkit SDK for Node with the correct values, and exports the created Chatkit instance, so that we can re-use it elsewhere with a single import.

Open the create-environment.js file. It is a script that contains a single function create() that will perform the following in your Chatkit instance:

  • Create two users, alice and bob. You will later be able to log in using their user IDs.
  • Create a private room with the ID alice;bob with alice and bob as members of this room.
  • Create a series of messages between the two users in the chatroom created in the previous step.

To run this script, type npm run create-environment in your terminal. A few seconds later the script should finish successfully after creating everything.

These are only a few of the operations you can do with Chatkit Node SDK. You can also do most of them using client SDKs, such as the one for Swift. The only operation that is exclusive to the server is createUsers. User management is considered an admin operation and is not exposed to clients. You can read more about what’s available for you in the Chatkit Node SDK in the docs.

You’re done with the Node SDK for now, but before moving onto the app part of this guide, have a look at another feature of the Chatkit dashboard - the instance inspector.

Using the instance inspector

Go back to your Chatkit dashboard, and select your instance, and click on the Console tab. This is your instance inspector, and is a helpful tool that helps you quickly glance at your Chatkit setup. You can use it to see the users, rooms, messages, and roles. In your case, you should see two users alice and bob who are both members of the room named Alice A, Bob B.

Although the instance inspector lets you perform some of the user actions that you can do with the SDKs, you’ve done it with the Node SDK as it’s more similar to how you would do it in a “real” app.

instance inspector

Developing the app

Running the app

By now you should be familiar with the Chatkit Node SDK, the instance locator, and the dashboard, and have the chatroom set up with two users. You will now integrate Chatkit in your iOS application, and see some messages coming through.

Open Xcode, and try running the app. You are greeted with the login screen. When you type in a user ID - for example alice - one of the users you've created earlier - and press the Login button you are taken to an empty chatroom screen.

Instantiating Chatkit in AppDelegate

We recommend you create a strong reference to the Chatkit instance and maintain a connection to Chatkit from a central place, where it can be accessed from everywhere in the app. In our case, that’s the App Delegate.

First, open the file AppDelegate.swift, and import the Chatkit package - PusherChatkit.

1
 import PusherChatkit

Now add two class variables to the AppDelegate class - chatManager, and currentUser:

1
2
3
 class AppDelegate: UIResponder, UIApplicationDelegate {
 
   var window: UIWindow?
4
5
   public var chatManager: ChatManager? 
   public var currentUser: PCCurrentUser?
6
   ...

The chat manager is your initial entry point into Chatkit, and the Current User will be your user’s own context after you've connected to Chatkit. In Chatkit, you will make all API interactions as a user you’re connecting with.
We created our first users earlier with the server SDK.

Add a new method to the AppDelegate class named initChatkit. that takes two arguments -userName, and callback. This function will initialise the chat manager and establish a connection to Chatkit.
You will call initChatkit from elsewhere in the app after logging in, and it will act as your main way to access Chatkit.

1
2
3
4
5
6
7
 class AppDelegate: UIResponder, UIApplicationDelegate {
 
   var window: UIWindow?
 
   public var chatManager: ChatManager? 
   public var currentUser: PCCurrentUser?
 
8
9
10
11
12
13
14
   func initChatkit(_ userId: String, _ callback: @escaping (_ currentUser: PCCurrentUser) -> Void){
     self.chatManager = ChatManager(
       instanceLocator: "YOUR_INSTANCE_LOCATOR",
       tokenProvider: PCTokenProvider(url: "YOUR_TOKEN_PROVIDER_ENDPOINT"),
       userID: userId
     ) 
   }
15
   ...

The ChatManager constructor takes 3 arguments:

  • instanceLocator defines which instance it’s connecting to. Copy it from your dashboard.
  • tokenProvider is the object that fetches valid user tokens.
  • userId is the ID of the Chatkit user that your app will connect as. The user needs to exist in your Chatkit instance before connecting with its ID.

The currentUser that you get after successfully connecting is the user with that ID. In your app you have created 2 valid users - alice and bob - and will connect as one of them.

A word on Token Providers

To make any request to the Chatkit API, you will require a valid token, that is signed by your secret key. You will usually generate these tokens in your backend, using the server SDK.
In the client you can use an instance of PCTokenProvider. It takes an endpoint it requests token provides from, and theChatManager will use use these tokens to perform your actions.

For now, we provide you with a Test Token Provider endpoint, that you can enable for testing and development purposes. In a later step you’ll implement a real token provider using the server SDK.

To enable the test token provider, you need to check the box in the dashboard, in the Credentials tab, below the instance locator and the key - as you can see in the screenshot below. The dashboard generates you a Test Token Provider endpoint. Replace the value of url with your newly generated endpoint.

You can read more about how Chatkit performs authentication in the docs.

test token provider inspector

Connecting to Chatkit

Now that you have instantiated your ChatManager, you can use it to connect to the Chatkit service, which will give you the current user when successfully connected.
The chatManager.connect() call takes aPCChatManagerDelegate as an argument, and a callback for your currentUser.

The chat manager delegate lets you react to events relating to your Chatkit connection, such as presence changes, room existence and membership updates, and more. All options are listed in the docs.

For now, just create an empty extension for AppDelegate class that implements thePCChatManagerDelegate protocol.

1
 extension AppDelegate: PCChatManagerDelegate {}

All of the available methods in the PCChatManagerDelegate protocol come with empty implementations, so you don’t need to implement any of them at this point.

Now, finish the initChatkitmethod implementation by calling connect, and passing self as the delegate argument:

1
2
3
4
5
6
 func initChatkit(_ userId: String, _ callback: @escaping (_ currentUser: PCCurrentUser) -> Void){
   self.chatManager = ChatManager(
     instanceLocator: "YOUR_INSTANCE_LOCATOR",
     tokenProvider: PCTokenProvider(url: "YOUR_TOKEN_PROVIDER_ENDPOINT"),
     userID: userId
   )
7
8
9
10
11
12
13
14
   chatManager!.connect(delegate: self) { (currentUser, error) in
   guard(error == nil) else {
     print("Error connecting: \(error!.localizedDescription)")
     return
   }
   self.currentUser = currentUser
   callback(currentUser!)
   }
15
} 

Implementing the chatroom

Next, open the file ChatroomController.swift. This is your main chatroom. Same as you did with AppDelegate.swift, you will need to import PusherChatkit before using it.

1
 import PusherChatkit

Now you can initiate Chatkit. In the code, find the mark TODO - Initiate Chatkit with your user ID in the viewDidLoad method, and invoke theinitChatkit method on the appDelegate:

1
2
3
4
5
6
 override func viewDidLoad() {
   super.viewDidLoad()
   appDelegate = (UIApplication.shared.delegate as! AppDelegate)
   messagesTableView.delegate = self
   messagesTableView.dataSource = self
   //TODO: Initiate Chatkit with your user ID   
7
8
9
   appDelegate.initChatkit(self.userId!) { (currentUser) in
 
   }
10
 }

The self.userId variable is set for you when transitioning into the ChatroomViewController class from LoginViewController.
You will use thecurrentUser you get in the callback to subscribe to a room.

Handling incoming messages

Before subscribing to a room, you need to be able to handle its events, such as new messages being received. You can handle all room events by implementing the PCRoomDelegate protocol.
Do so by creating an extension for the ChatroomViewController class. To handle incoming messages you will also need to implement the onMultipartMessage hook:

1
2
3
4
5
 extension ChatroomViewController: PCRoomDelegate {
  func onMultipartMessage(_ message: PCMultipartMessage) {
    print("Message received!")
  }
 }

Subscribing to a room

All rooms your user is a member of are available in theroomsproperty of the currentUser. For now, your user only has a single room, so you can get it like this:

1
let firstRoom = currentUser.rooms.first!

You can verify your room’s ID matches the room you have created earlier by checking its id property - firstRoom.id.
To subscribe to the room, you need to call the subscribeToRoomMultipartfunction on currentUser , passing it that room:

1
2
3
4
5
6
7
8
 override func viewDidLoad() {
   super.viewDidLoad()
   appDelegate = (UIApplication.shared.delegate as! AppDelegate)
   messagesTableView.delegate = self
   messagesTableView.dataSource = self
   //TODO: Initiate Chatkit with your user ID
   
   appDelegate.initChatkit(self.userId!) { (currentUser) in
9
10
11
12
13
14
15
16
17
18
19
 
     let firstRoom = currentUser.rooms.first!
 
     // Subscribe to the first room
     currentUser.subscribeToRoomMultipart(room: firstRoom, roomDelegate: self, completionHandler: { (error) in
       guard error == nil else {
         print("Error subscribing to room: \(error!.localizedDescription)")
         return
       }
       print("Successfully subscribed to the room!")
     })
20
21
   }
 }

Displaying messages

Lastly, make sure the messages are being stored and displayed in the table as they arrive. Create a variable messages in theChatroomController class that holds an array ofPCMultipartMessage:

1
 var messages = [PCMultipartMessage]()

Then, implement displaying of messages in the UITableViewDataSourceextension. You will need to replace the empty implementations of the two methods in it:

1
2
 extension ChatroomViewController: UITableViewDataSource {    
   func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
3
     return 0
4
     return messages.count
5
6
7
8
   }
 
   func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath)
9
10
11
12
13
14
15
16
17
18
19
20
     let message = messages[indexPath.row]
     let senderDisplayName = message.sender.displayName
     var messageText = ""
 
     switch message.parts.first!.payload {
       case .inline(let payload):
         messageText = payload.content
      default:
          print("Message doesn't have the right payload!")
     }
 
     cell.textLabel?.text = "\(senderDisplayName): \(messageText)"
21
22
23
     return cell
   }
 }

The item count you need to return is the size of your messages array.
As each Chatkit message can consist of up to 10 parts of various types -inline, attachment, orurl. You need to take the first part’s payload and assume its type is inline for the purposes of this guide.
Read more about handling different message payloads here.

Apart from the payload itself, you’ll likely be interested in who sent the message - for that you have the sender property. This will render the text of our messages as Alice A: Hello world.

The last part of handling incoming messages is to insert them into the messages array and the corresponding table view. In the PCRoomDelegateextension you created earlier - modify it to update the table when messages are received:

1
2
3
 extension ChatroomViewController: PCRoomDelegate {
   func onMultipartMessage(_ message: PCMultipartMessage) {
     print("Message received!")
4
5
6
7
     DispatchQueue.main.async {
       self.messages.append(message)
       self.messagesTableView.reloadData()
     }
8
9
   }
 }

You may have noticed that the logic for handling messages is inside a DispatchQueue.main.async block. This is because the message handler is running on a background thread, and to display it you need to execute it on the main thread.

By now you can test receiving messages by opening the app in the simulator, logging in with either alice or bob user ID, and send a test message to your chatroom using the instance inspector in the dashboard.

Sending messages

To send messages from the app yourself, you need to implement thesendMessage method. It’s called by theonSendClicked action that’s tied to the button click.

 func sendMessage(_ message: String) {
   appDelegate.currentUser!.sendSimpleMessage(
     roomID: appDelegate.currentUser!.rooms.first!.id, 
     text: message, 
     completionHandler: { (messageID, error) in
       guard error == nil else {
         print("Error sending message: \(error!.localizedDescription)")
         return
       }
       DispatchQueue.main.async {
         self.textEntry.text = ""
       }
     }
   )
 }

Try running the app and sending a message now.

Extra: Implement the Token Provider

Congratulations! You have successfully implemented Chatkit in an iOS app.
If you recall from earlier - we used the Test Token Provider endpoint we enabled in the dashboard.
That’s a great service for testing, but in production is not particularly secure - anyone requesting a token will be granted one for any user ID that they choose.

In practice, you’ll want to issue your own tokens, and tie them to your existing authentication mechanism.

Change your server/server.js to add the following endpoint:

1
2
3
4
5
 const chatkit = require('./chatkit')
 const Express = require('express')
 
 const app = new Express()
 
6
7
8
9
10
11
 app.post("/auth", (req, res) => {
   const authData = chatkit.authenticate({
     userId: req.query.user_id
   })
   res.status(authData.status).send(authData.body)
 })
12
13
14
15
 
 app.listen(3000, () => {
   console.log("Token providing endpoint listening at: http://localhost:3000/auth")
 })

This will listen for POST requests to http://localhost:3000/auth and issue tokens to it.
At the moment this endpoint does not handle any additional security, but you can easily add it by performing a check with your own backend systems before calling chatkit.authenticate(). A common way to authenticate it would be to take the user ID from the session you created in your application when the user logged in.

You can start the server by running npm run serve-token, and replacing thetokenProvider's url field in your app’sAppDelegate class with your own endpoint -http://localhost:3000/auth.

Note that the localhost URL will only work on a simulated device running on your machine. To test this on a real device, you can use something likeNgrok to create yourself a public URL, or deploy the service to be publicly accessible, for example on Heroku.

Conclusion and next steps

That’s it! You have configured your brand new Chatkit instance and familiarized yourself with the parts that make up Chatkit, including the server and client SDKs, and how authentication works.

You can find the complete project in the same repository, in the complete branch - just replace the instance locator and secret key!

For more detailed information on what else is possible, check out the rest of the docs: