Build an iOS chat app with Pusher

iOS-Chat-App-Header.jpg

In this tutorial, you will learn how to easily build an iOS chat app by using Pusher to send and show realtime messages in the UI.

Introduction

We thought we’d create a walkthrough of how to easily build an iOS chat app with Pusher. Together we will build a group chat application, leading you through how to use Pusher to send and show realtime messages in your UI.

The app we’re building

The app we’re building is a simple chat application that uses Pusher to send and receive messages.
It has two screens – the “Login” screen where we enter our Twitter username, and the “Chat” screen where we do the messaging.

Setting up our project with XCode

If you haven’t yet, create a new application on XCode. By default the wizard offers you to create a Single View Application for iOS, and that’s perfectly fine. Once you’ve done that, you’ll need to prepare the dependencies for the app. The dependencies you need are Pusher Swift for interaction with Pusher, and AlamofireImage for performing network requests, and loading images over the network.

The easiest way install dependencies is by using CocoaPods. If you don’t have CocoaPods installed you can install them via RubyGems.

1gem install cocoapods

Then configure CocoaPods in our application. First initialise the project by running this command in the top-level directory of your XCode project:

1pod init

This will create a file called Podfile. Open it, and make sure to add the following lines specifying your app’s dependencies:

1# Uncomment the next line to define a global platform for your project
2# platform :ios, '9.0'
3
4target 'Pusher Chat Sample iOS' do
5  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
6  use_frameworks!
7
8  # Pods for Pusher Chat Sample iOS
9  pod 'PusherSwift'
10  pod 'AlamofireImage' //need that for networking
11
12end

And then run pod install to download and install both dependencies.

1pod install

CocoaPods will ask you to close XCode if it’s currently running, and open the newly generated .xcworkspace file. Do this now, and XCode will open with your project configured.

Creating the Login View

For our login feature we’ll just create a simple page with a field to enter a twitter handle and a login button.

First rename our scene to “Login Scene”, and then drag the two elements onto it.
Also rename the ViewController to LoginViewController.

Control-drag the each element into the LoginViewController class to create the IBOutlet (for the TextView) and the IBAction for the button.

Name the IBOutlet twitterHandle and IBAction loginButtonClicked.

In your LoginViewController.swift add the following logic to the loginButton function:

1@IBAction func loginButtonClicked(_ sender: Any) {
2       if(twitterHandle.hasText){
3           let messagesViewController = self.storyboard?.instantiateViewController(withIdentifier: "chatViewController") as! ChatViewController
4           messagesViewController.twitterHandle = twitterHandle.text!
5           self.present(messagesViewController, animated:true)
6       }
7       else{
8           print("No text in textfield")
9       }
10}

This will grab the current text in the twitterHandle field and set it to the ChatViewController, and transition to the Chat screen.

Chat View

But the ChatViewController doesn’t exist yet! Create a new ViewController in the Storyboard and the corresponding ChatViewController.swift class.
Add to it a TableView, a Text Field, and a Button as in the example.

Listening to messages

We will listen to new messages in realtime by subscribing to the chatroom channel and listening to events tagged new_message.
Pusher channels can support unlimited number of message types, but in our case we are only interested the single one.

In viewDidLoad create your Pusher instance – and copy your setup details from the Pusher Dashboard. It shoud look like this:

1pusher = Pusher(
2          key: "abcdefghijklmnopqrstuvwxyz"
3)

Then subscribe to the chatroom channel, and bind to the new_message events, printing their messages to the console. Lastly, connect to Pusher.

1let channel = pusher!.subscribe("chatroom")
2let _ = channel.bind(eventName: "new_message", callback: { (data: Any?) -> Void in
3
4    if let data = data as? [String: AnyObject] {
5
6        let text = data["text"] as! String
7        let author = data["name"] as! String
8        print(author + ": " + text)
9    }
10})
11pusher!.connect()

Now that we’ve subscribed and listening to the events, we can send some events to test it out. The easiest way to do this is by using Pusher’s Debug Console – in your app’s Dashboard. Have the application running – Simulator is fine.
Click Show Event Creator button, and change the name of Channel to chatroom, and change the Event to new_message – what we’re listening to in the app.
Now change the Data field to something like:

1{
2  "name": "John",
3  "text": "Hello, World!"
4}

And click Send event. In the XCode’s console, you should see the message printed out:

1John: Hello, World!

Presenting messages in a table

Now, let’s show the messages as they arrive in the UITableView.
We will create a Prototype cell in the UITableView in the Storyboard, and specify a class for it.

Create a MessageCell.swift class and make it extend UITableViewCell. This will represent a single chat message as a row in our table. Drag the outlets for authorAvatar, authorName, and messageText into the class. This

1import Foundation
2import UIKit
3
4class MessageCell: UITableViewCell {
5    @IBOutlet var authorAvatar: UIImageView!
6    @IBOutlet var authorName: UILabel!
7    @IBOutlet var messageText: UILabel!
8}

Now create a Message.swift which will hold a struct representing a single Message object. It just needs to hold two strings, for the author and message.

1import Foundation
2
3struct Message {
4
5    let author: String
6    let message: String
7
8    init(author: String, message: String) {
9        self.author = author
10        self.message = message
11    }
12}

Back in the ChatViewController.swift, make the class implement the protocols UITableViewDataSource and UITableViewDelegate alongside UIViewController:

1class ChatViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

To make it compile, you’ll need to implement the following methods – first one to let the tableView know how many items it holds:

1func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
2       return array.count
3   }

And the second one that will create a MessageCell object:

1func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
2
3    let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath) as! MessageCell
4    return cell
5}

Then we need to add some logic that will actually present the data in a cell. Add these lines to the second method:

1let message = array.object(at: indexPath.row) as! Message
2
3cell.authorName.text = message.author
4cell.messageText.text = message.message
5
6let imageUrl = URL(string: "https://twitter.com/" + message.author + "/profile_image")
7cell.authorAvatar.af_setImage(withURL: imageUrl!)

First we set up the text in the author and message labels, and lastly we use the AlamofireImage library to load the image from Twitter avatar into the authorImage field.

Sending messages from the app

Building the serverside component in NodeJS

So far, we’ve created a client that receives items. But what about sending them? We’ll do that next.
First, we’ll need a server-side component that receives messages and sends them back to Pusher.

We prepared a simple NodeJS application that will serve that purpose. You can find it here.

First clone the repository and CD into its directory. Then run npm install to setup dependencies.

Then open app.js and change the Pusher initialisation fields there to include your App ID, key and secret. You can copy these from your Pusher Dashboard – the Getting Started tab will have everything you need.

Once you’ve done that you can launch the app by running node app.js.

If your iOS app is running on your simulator, and your Node app is running the server, you should be able send a test message via the cURL command:

1$ curl -X "POST" "http://localhost:3000/messages" -H "Content-Type: application/json; charset=utf-8" -d $'{"name": "Pusher","text": "Hello, Node!"}'

If everything works as it should, you should see the new message appear in your app.

Building the app component

The last thing to do is to create the function that triggers and sends the message to us.

First make sure your Text Field and Button have their corresponding outlets in ChatViewController.swift:

1@IBOutlet var message: UITextField!
2@IBAction func send(_ sender: Any) {
3
4       if(message.hasText){
5           postMessage(name: twitterHandle, message: message.text!)
6       }
7}

Finally, we can implement the postMessage function that calls our NodeJS endpoint to trigger a new message over Pusher:

1func postMessage(name: String, message: String){
2
3        let params: Parameters = [
4            "name": name,
5            "text": message
6        ]
7
8        Alamofire.request(ChatViewController.MESSAGES_ENDPOINT, method: .post, parameters: params).validate().responseJSON { response in
9
10            switch response.result {
11
12            case .success:
13                print("Validation successful")
14            case .failure(let error):
15                print(error)
16            }
17        }
18}

Try it out!

If you are running the Node server locally XCode might not allow you to make the request. You can get around this by adding App Transport Security Settings to your Info.plist file and set Allow Artibrary Loads to YES.

Get Pushing

Hopefully you have found this a straightforward example of how to build an iOS chat app with Pusher. There are many ways you can extend this tutorial for an improved application:

  • Use Pusher client events to send messages from one client to another. You could use our webhooks to notify your server when messages are sent, allowing you to persist them in your database.
  • Use Pusher presence channels to create live user lists and show who’s online in realtime.
  • Use our REST API to determine whether a user is connected to Pusher. If they are, go ahead and send them a message as normal. If not, send them a native push notification leading them back to your app.

Even with such a basic app, hopefully I have shown you how easy and few lines of code it is to drop in Pusher to any iPhone app. Feel more than free to let us know what you end up building with Pusher and iPhone!