Chatkit Swift Quick Start

This quick start will take you through the basics of using Chatkit with Swift, specifically iOS.

Our aim is to help you build your first (minimal) Chatkit application. It should take about 15 minutes to follow along (or a few minutes to read, if you’d prefer).

If you're looking for the complete code, scroll to the bottom.

What Is Chatkit?

Great question 😏 !

Chatkit is a hosted API that helps you build impressive chat features into your applications with less code. It offers you features like:

  • Group chat
  • One-to-one chat
  • Private chat
  • Typing indicators
  • "Who's online" presence
  • Read receipts
  • Photo, video, and audio messages

Using our cross-platform SDKs, all chat data is sent through our hosted API where we manage chat state and broadcast it to your clients:

We manage your chat data for you, making it easy to show historical messages, typing indicators, etc. with little to no custom server code. You'll never have to worry about scale, reliability, or infrastructure, we take care of it all for you.

Getting Started

To get started with Chatkit, you’ll first need to create a Chatkit instance.

You can have as many instances as you want. To create your first, head to the dashboard, hit Create new +, then give your instance a name. I will call mine “Getting Started App”:

Keys

Once you’ve created your Chatkit instance, head to the Keys tab and take note of your Instance Locator:

You’ll need this later to connect to Chatkit.

Create a User

Before someone can connect to a Chatkit instance, you must first create a Chatkit user.

Normally, you would do this programmatically on your server, using one of our server SDKs:

1
2
3
4
5
6
7
8
9
10
11
// Example Node code. Do not copy

const chatkit = new Chatkit.default({
  instanceLocator: "YOUR INSTANCE LOCATOR",
  key: "YOUR SECRET KEY"
})

chatkit.createUser({
  id: "bookercodes",
  name: "Alex Booker"
})

That being said, you can also create users manually from the Inspector tab.

I will call mine “bookercodes”:

Create a Room

With Chatkit, all messages are sent to a room. You can have as many rooms as you want.

For example, if you want to build a one-to-one chat, we encourage you to create a room with just two users.

Rooms can be created programmatically or from the Inspector tab. I will use the Inspector tab and call mine “General”:

Remember, while the Inspector is a handy tool to get started and occasionally debug your app, we didn’t design it for production use.

Going forward, we encourage you to create users and rooms programmatically.

Enable the Test Token Provider

Chatkit has been designed to integrate seamlessly with your existing authentication system.

That being said, to help you get started quickly, we provide a test token provider which you can enable in the Settings tab:

Once enabled, take note of your test token provider endpoint. You’ll need it in the next step.

Please remember, the test token provider is insecure and unsuitable for production. To learn more about production-grade authentication, see our authentication documentation.

Setting Up and Installing Chatkit

If you’re following along, create a new iOS Single View App.

Give it a name and be sure to select Swift as the language.

Next we need to get the Chatkit SDK ready to use in our app. You can use Carthage or CocoaPods to do this.

If you’re using CocoaPods, run pod init in the root of your newly created project. In the generated Podfile, add PusherChatkit.

1
2
3
4
target 'YOUR_TARGET_NAME' do
  use_frameworks!
  platform :ios, '11.0'
 
5
  pod 'PusherChatkit'
6
end

Run pod install and open the created workspace.

If you’re using Carthage, create a Cartfile (touch Cartfile) in the root of your newly created project. In the Cartfile, add the following:

github "pusher/chatkit-swift"

Run carthage update and wait for the SDK to be cloned and built.

Initialise Chatkit

Now that we’ve got the SDK installed we can start adding some Chatkit-related code to the app.

First up we’ll need to import the SDK. Add import PusherChatkit at the top of ViewController.swift along with some code to initialise our ChatManager.

1
2
3
4
import PusherChatkit
 
class ViewController: UIViewController {
 
5
6
    var chatManager: ChatManager!
    var currentUser: PCCurrentUser?
7
8
9
 
    override func viewDidLoad() {
        super.viewDidLoad()
10
11
12
13
14
        chatManager = ChatManager(
            instanceLocator: "YOUR INSTANCE LOCATOR",
            tokenProvider: PCTokenProvider(url: "YOUR TEST TOKEN ENDPOINT"),
            userId: "YOUR USER ID"
        )
15
16
17
    }
 
}

Remember to replace "YOUR TEST TOKEN ENDPOINT", "YOUR INSTANCE LOCATOR", and "YOUR USER ID" with your own values.

Connect to Chatkit

Beneath chatManager, connect to Chatkit and, provided there is no error, store the currentUser value in the currentUser property:

1
2
3
4
5
6
7
8
9
10
chatManager.connect(delegate: self) { [unowned self] currentUser, error in
    guard error == nil else {
        print("Error connecting: \(error!.localizedDescription)")
        return
    }
    print("Connected!")

    guard let currentUser = currentUser else { return }
    self.currentUser = currentUser
}

You can see that we’ve specified self, where self is our ViewController, as a delegate . To make this work, add the following at the bottom of ViewController.swift:

extension ViewController: PCChatManagerDelegate {}

If you run the app at this point you should see "Connected!" in the logs. If you don’t, make sure you’ve got your instance locator, test token endpoint and user ID all correct.

Subscribe to a Room

When the call to connect has finished, you’ll receive the currentUser. Almost all interactions with Chatkit happen on currentUser.

When we created our room via the dashboard inspector earlier on our created user was automatically added as a member of the room because they created the room. As a result, the currentUser's rooms property will be populated with the room we created so we can subscribe to it straight away.

1
2
3
4
5
6
7
8
9
10
chatManager.connect(delegate: self) { [unowned self] currentUser, error in
    guard error == nil else {
        print("Error connecting: \(error!.localizedDescription)")
        return
    }
    print("Connected!")
 
    guard let currentUser = currentUser else { return }
    self.currentUser = currentUser
 
11
12
13
14
15
16
17
18
19
20
    currentUser.subscribeToRoom(
        room: currentUser.rooms[0],
        roomDelegate: self
    ) { err in
        guard error == nil else {
            print("Error subscribing to room: \(error!.localizedDescription)")
            return
        }
        print("Subscribed to room!")
    }
21
}

Note that we’re passing self as the roomDelegate. To make the ViewController conform to the delegate we’ll add this to the bottom of ViewController.swift:

1
2
3
4
5
extension ViewController: PCRoomDelegate {
    func newMessage(message: PCMessage) {
        print("\(message.sender.id) sent \(message.text)")
    }
}

This will print out received messages to the console.

Now, if you send a message from the Inspector tab of the dashboard, you’ll see it in the logs for your iOS app:

Watch this as well - if you run the app again, you’ll see that newMessage is called retroactively up to 20 times.

In practice, this allows you to easily show your user recent messages.

Don’t want to show old messages? Set messageLimit to 0 in the call to subscribeToRoom. Chatkit is completely flexible by design.

1
2
3
currentUser.subscribeToRoom(
    room: currentUser.rooms[0],
    roomDelegate: self,
4
    messageLimit: 0
5
)

Send Your Own Message

Again, almost all interactions happen on the currentUser.

To send a message, we call currentUser.sendMessage with the ID of the room that we want to send the message to and the content of the message (the text).

We’ll add a button to our app that will send a message with some pre-defined text to the first room in the list of rooms that the current user is a member of.

1
2
3
4
class ViewController: UIViewController {
 
    var currentUser: PCCurrentUser?
 
5
6
7
8
9
10
11
12
13
14
15
16
   @IBAction func sendMessageButton(_ sender: Any) {
       self.currentUser!.sendMessage(
           roomId: self.currentUser!.rooms[0].id,
           text: "Hello from iOS 👋"
       ) { messageId, error in
           guard error == nil else {
               print("Error sending message: \(error!.localizedDescription)")
               return
           }
           print("Message sent!")
       }
   }

Once you’ve added this you can run the app, click the "Send message" button, and you’ll see the message appear in the logs along with a "Message sent!" log message.

If you run the app in a second simulator then you can send a message from one device and see the message be received by the other one, as shown below.

In this case, we're just sending the text "Hello from iOS 👋" but Chatkit also supports file attachments. Sending photo, video, and audio messages has never been easier.

Throwing It All Together

When you throw it all together with a simple text input and a table view, you end up with this:

Here’s the complete code you need in the ViewController to get something that looks like the above. Don’t forget that you’ll need to wire up the messageInput and messagesTable to Interface Builder to make sure everything works.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import UIKit
import PusherChatkit

class ViewController: UIViewController {
    var chatManager: ChatManager!
    var currentUser: PCCurrentUser?
    var messages: [PCMessage] = []
    var defaultFrame: CGRect!

    @IBAction func sendMessageButton(_ sender: Any) {
        self.currentUser!.sendMessage(
            roomId: self.currentUser!.rooms.first!.id,
            text: self.messageInput.text!
        ) { messageId, error in
            guard error == nil else {
                print("Error sending message: \(error!.localizedDescription)")
                return
            }
            print("Message sent!")
        }
        self.messageInput.text!.removeAll()
    }

    @IBOutlet weak var messageInput: UITextField!
    @IBOutlet weak var messagesTable: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        messagesTable.transform = CGAffineTransform(scaleX: 1, y: -1)
        defaultFrame = self.view.frame
        NotificationCenter.default.addObserver(self, selector: #selector(keyBoardWillShow), name: .UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyBoardWillHide), name: .UIKeyboardWillHide, object: nil)

        messagesTable.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
        messagesTable.dataSource = self

        chatManager = ChatManager(
            instanceLocator: "YOUR INSTANCE LOCATOR",
            tokenProvider: PCTokenProvider(url: "YOUR TEST TOKEN ENDPOINT"),
            userId: "YOUR USER ID"
        )

        chatManager.connect(delegate: self) { [unowned self] currentUser, error in
            guard error == nil else {
                print("Error connecting: \(error!.localizedDescription)")
                return
            }
            print("Connected!")

            guard let currentUser = currentUser else { return }
            self.currentUser = currentUser

            currentUser.subscribeToRoom(room: currentUser.rooms[0], roomDelegate: self) { err in
                guard error == nil else {
                    print("Error subscribing to room: \(error!.localizedDescription)")
                    return
                }
                print("Subscribed to room!")
            }
        }
    }

    func moveViewsWithKeyboard(height: CGFloat) {
        self.view.frame = defaultFrame.offsetBy(dx: 0, dy: height)
    }

    @objc func keyBoardWillShow(notification: NSNotification) {
        let frame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        moveViewsWithKeyboard(height: -frame.height)
    }

    @objc func keyBoardWillHide(notification: NSNotification) {
        moveViewsWithKeyboard(height: 0)
    }
}

extension ViewController: PCChatManagerDelegate {}

extension ViewController: PCRoomDelegate {
    func newMessage(message: PCMessage) {
        print("\(message.sender.id) sent \(message.text)")
        messages.insert(message, at: 0)
        DispatchQueue.main.async {
            self.messagesTable.reloadData()
        }
    }
}

extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return messages.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        cell.contentView.transform = CGAffineTransform(scaleX: 1, y: -1)
        let message = self.messages[indexPath.row]
        cell.textLabel?.text = "\(message.sender.displayName): \(message.text!)"
        return cell
    }
}

Next Steps

The quick start is aimed at teaching you the key components of Chatkit. You learned about instances, rooms, users, and authentication.

What would you like to learn more about?

Did you find this document useful?

We are always striving to create the most accurate and informative docs as possible. If there is something especially wrong (or right) here then please let us know.