How to build a realtime user’s status update in iOS
Tags: ios, realtime
Published: 2018-02-20 09:35:24

---
title: How to build a realtime user’s status update in iOS
published: true
description: 
tags: IOS, realtime
---


When building chat applications, it is not uncommon to see something like a friends’ list with the status of the friend. Applications like WhatsApp have this feature and it is very useful to check the status of your friend and know if it is wise to send them a message at that point.

We are going to be building a similar feature in a make-believe iOS chat application. We are going to be using [Pusher](https://pusher.com) to implement realtime features to the application so that when someone posts a new status update you can see it change in realtime.

Here is a screen recording of how our application will look when we are done.

![](https://blog.pusher.com/wp-content/uploads/2017/10/realtime-status-update-ios-1.gif)

To follow along with this article, you must have the following requirements:
– Some knowledge of Swift 3
– Knowledge on how to use Xcode
– Basic JavaScript knowledge
– [NPM](https://www.npmjs.com/get-npm) and [CocoaPods](https://guides.cocoapods.org/using/getting-started.html) installed on your machine.
– Basic knowledge on the terminal (command-line)
– A Pusher application (You will need the application’s ID, Secret, Key, and Cluster). [Create a Pusher](https://pusher.com) account if you do not have one currently.

## Getting our project ready

To get started, we need to create the iOS project and then install some dependencies that’ll be needed for the application to function correctly. Let us begin.

**Setting up our project in Xcode**
Launch Xcode on your machine and create a new project. Create a single application project and follow the wizard until you get to the main storyboard. Once you are there, exit Xcode.

In your terminal, `cd` to the Xcode project directory and then run the command below:

```
    $ pod init

```

This will create a `Podfile` inside the root of your application. The Podfile is where we will define Cocoapods dependencies. Open in your text editor and replace with the following:

```
    platform :ios, '8.4'

    target 'project_name' do
      use_frameworks!
      pod 'PusherSwift', '~> 4.0'
      pod 'Alamofire', '~> 4.4'
    end

```

In the above, we have just specified the dependencies we want CocoaPods to install into our application. Do not forget to substitute the _project\_name_ for your actual project name.

Now go to the terminal and run the command:

```
    $ pod install

```

This should install all of the dependencies and libraries we have specified in our `Podfile`. Great! Finally, open the project directory and double-click the `.xcworkspace` file in the directory to launch your project workspace in Xcode.

## Creating the User Interface of our realtime iOS application

Now that we have created the project in Xcode and have successfully installed all the dependencies, the next thing we will want to do is create the user interface of our iOS application. Open the `main.storyboard` file in Xcode and let’s start designing the UI.

This is what we want to have at the end of this section:

![](https://blog.pusher.com/wp-content/uploads/2017/10/realtime-status-update-ios-2.png)

Add a Navigation Controller in your canvas and make it the root view controller. When you have done this, you then need to update the `TableViewController` attached to the Navigation Controller.

First, create a new class in Xcode using `ctrl+n`; the class name should be `FriendsViewController` and it should extend `UITableViewController`. Then, in the `main.storyboard` file, make sure you make the `TableViewController` use the `FriendsViewController` as its custom class.

![](https://blog.pusher.com/wp-content/uploads/2017/10/realtime-status-update-ios-3.png)

**Configuring the prototype cells**
Now that we have created the table view controller, we need to configure its cells to match what we are trying to achieve.

Click on “Prototype Cells” on the main storyboard file and make the attributes inspector look something close to the image below.

> 💡 **For the image**`**avatar.png**`\*\*, you can add a 45×45 pixel image to your Xcode project and use that as the image for the cell.\*\*

![](https://blog.pusher.com/wp-content/uploads/2017/10/realtime-status-update-ios-4.png)

One last thing we can do (which is completely optional) is changing the navigation bar color for our application. Let’s do that.

Open the `AppDelegate` class and in the `application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?)` method paste the following:

```swift 
    UINavigationBar.appearance().barTintColor = UIColor(red: 18.0/255.0, green: 140.0/255.0, blue: 126.0/255.0, alpha: 1.0)
    UINavigationBar.appearance().tintColor = UIColor.white
    UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]

```

With this, you have finished creating the UI for the application and all that is left is the functionality to support it. Let us do that now.

## Creating the functionality of our realtime iOS application

For the functionality, we will divide it into two parts. The first part will focus on adding the functionality for updating the status, and the second part will focus on making the updates realtime.

**Creating the initial functionality: U\*\*\*\*pdate** **status**
Lets open `FriendsViewController` and make some modifications. The first modification will be adding an update “Status” button to the top right corner of the navigation bar.

Inside the `viewDidLoad` method of the controller, add the code below:

```swift 
    navigationItem.title = "Friends List"
    navigationItem.rightBarButtonItem = UIBarButtonItem(
        title: "Status",
        style: .plain,
        target: self,
        action: #selector(showPopup(_:))
    )

```

The code above simply sets the title of the controller in the navigation bar and adds a button to the right side of the navigation bar.

If you notice, in the `action` parameter it points to a method `showPopup` so let us create this method. Add this method to the controller:

```swift 
    public func showPopup(_ sender: Any) {
        let alertController = UIAlertController(
            title: "Update your status",
            message: "What would you like your status to say?",
            preferredStyle: .alert
        )

        alertController.addTextField(configurationHandler: {(_ textField: UITextField) -> Void in
            textField.placeholder = "Status"
        })

        alertController.addAction(UIAlertAction(title: "Update", style: .default, handler: {(_ action: UIAlertAction) -> Void in
            let status = (alertController.textFields?[0].text)! as String
            self.postStatusUpdate(message: status)
        }))

        alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))

        present(alertController, animated: true, completion: nil)
    }

```

So what we did here is, when the action is called and the `showPopup` method is invoked, the application will display a popup box that asks the user to input their status.

Right now, the popup calls a method `postStatusUpdate` which does not exist in our application. Let us create this method now.

In the view controller, add the method below:

```swift 
    public func postStatusUpdate(message: String) {
        let params: Parameters = ["username": username, "status": message]

        Alamofire.request(FriendsViewController.API_ENDPOINT + "/status", method: .post, parameters: params).validate().responseJSON { response in
            switch response.result {

            case .success:
                _ = "Updated"
            case .failure(let error):
                print(error)
            }
        }
    }

```

In this method, we are using the `Alamofire` library to make a request to an endpoint `FriendsViewController.API_ENDPOINT + "/status``"` (which does not yet exist). Right now, because we have not imported the Alamofire library nor defined `FriendsViewController.API_ENDPOINT` we will get errors.

At the top of the view controller, import the `Alamofire` library:

```
    import 'Alamofire'

```

Also, inside the class, after the class definition, add the following to declare the `API_ENDPOINT` which will point to the remote HTTP server.

```
    static let API_ENDPOINT = "http://localhost:4000";

```

> 💡 **The endpoint we are using now is a local server which will be created later on in the article. If you are using a remote server, you will need to replace this value with the URL of your server.**

So, right now, when you run the application and click the “Status” button it will bring a popup and you can enter your update. However, because we have not yet created a backend to respond to this call, it will fail and not do anything. We will get to that later in the article.

**Updating the default table view controller methods**
The table view controller comes with some methods by default, and we will quickly change them to fit our application.

Open the view controller and update the method `numberOfSections`. Make the return value 1. This will make sure that the first and only section is displayed.

Next, update the `tableView(tableView: UITableView, numberOfRowsInSection: section)` method and make the return value `friends.count`. This will make sure that the right amount of rows are created for each entry on the `friends` list.

To make the cells display the details of each friend, update the contents of the `tableView(tableView:UITableView, cellForRowAt indexPath:IndexPath)` method with the code below:

```swift 
    let cell = tableView.dequeueReusableCell(withIdentifier: "friends", for: indexPath)

    var status   = friends[indexPath.row]["status"]

    if status == "" {
        status = "User has not updated status!"
    }

    cell.detailTextLabel?.textColor = UIColor.gray

    cell.imageView?.image = UIImage(named: "avatar.png")
    cell.textLabel?.text = friends[indexPath.row]["username"]
    cell.detailTextLabel?.text = status

    return cell

```

The code above simply gets the current cell and updates the required cell labels with the status, username and image (in case you want to add another image).

Finally, add a new method to the view controller:

```swift 
    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 75.0
    }

```

This will just increase the row height of the table to be equal to **75.0**. This will make it easier to accommodate the contents of the cell.

## Using Pusher to add a realtime update status to our iOS application

Now, before we add the realtime online status updates using Pusher, we want to add some sort pseudo friends listing.

We will do the friends listing using Pusher. We will accomplish this by creating a class property that is not persistent, and in this variable, we will store details of anyone that comes online.

**Adding a pseudo friends list**
In the view controller, add some new properties:

```swift 
    var friends : [[String:String]] = []
    var username : String = ""
    var pusher : Pusher!

```

The `friends` property will store all the users who come online, the `username` property will store a random username for the current user, and the `pusher` property will store the Pusher library instance.

Now, in the `viewDidLoad` method, add the following code:

```swift 
    username = "Anonymous" + String(Int(arc4random_uniform(1000)))

    listenForRealtimeEvents()

    // --- Update online presence at intervals --- //
    let date = Date().addingTimeInterval(0)
    let timer = Timer(fireAt: date, interval: 1, target: self, selector: #selector(postOnlinePresence), userInfo: nil, repeats: true)
    RunLoop.main.add(timer, forMode: RunLoopMode.commonModes)

```

On line 1, we just assign the `username` property a random string as a username.

On line 3, we call a method `listenForRealtimeEvents` that does not yet exist (we will create this later).

And on line 6 – 8, we just basically added a looping call to the `postOnlinePresence` (also doesn’t exist yet). This call will basically update your online presence every second.

Let us create the `listenForRealtimeEvents` method now. Add the following code to the view controller:

```swift 
    private func listenForRealtimeEvents() {
        pusher = Pusher(key: "PUSHER_KEY", options: PusherClientOptions(host: .cluster("PUSHER_CLUSTER")))

        let channel = pusher.subscribe("new_status")
        let _ = channel.bind(eventName: "online", callback: { (data: Any?) -> Void in
            if let data = data as? [String: AnyObject] {
                let username = data["username"] as! String

                let index = self.friends.index(where: { $0["username"] == username })

                if username != self.username && index == nil {
                    self.friends.append(["username": username, "status": "No Status"])
                    self.tableView.reloadData()
                }
            }
        })

        pusher.connect()
    }

```

In the method we just created, we just instantiated the Pusher library with our Pusher key and application cluster. Then we subscribed to a Pusher channel called **new\_status** and, on that channel, we started listening for the event called **online.**

In the callback, when the event listener is triggered, we get the username from the event. We then check if there is username in the list of `friends` that matches. If there isn’t, we append it to the friends’ list and reload the table data.

So, in summary, every time someone comes online it appends the name to the friends’ list and reloads the table view.

Next, we will create the method `postOnlinePresence` that will periodically post the current users online presence so others can pick it up. In the view controller add the code below:

```swift 
    public func postOnlinePresence() {
        let params: Parameters = ["username": username]

        Alamofire.request(FriendsViewController.API_ENDPOINT + "/online", method: .post, parameters: params).validate().responseJSON { response in
            switch response.result {

            case .success:
                _ = "Online"
            case .failure(let error):
                print(error)
            }
        }
    }

```

The code above simply hits an endpoint and thus marks the user as online.

**Adding status updates to the application with Pusher**
The final part of our iOS application will be adding the listener for the updates so that every time someone updates their status, the update is added.

To do this, open the `listenForRealtimeEvents` method and add the following after instantiating the pusher variable:

```swift 
    let channel = pusher.subscribe("new_status")
    let _ = channel.bind(eventName: "update", callback: { (data: Any?) -> Void in
        if let data = data as? [String: AnyObject] {
            let username = data["username"] as! String

            let status = data["status"] as! String

            let index = self.friends.index(where: { $0["username"] == username })

            if index != nil {
                self.friends[index!]["status"] = status
                self.tableView.reloadData()
            }
        }
    })

```

The above code creates a listener for the **update** event to the **new\_status** channel. When the event is triggered, the callback checks if the username is part of the friends’ list. If it is, it updates the status of that entry and reloads the table view data.

Now we have successfully added the realtime features to our application. The next thing we want to do is create a backend to help us actually trigger Pusher events that can be picked up by our iOS application.

## Creating the NodeJS backend for our realtime iOS status updates app

Create a directory for the web application and then create some new files:

```javascript 
**index.js**

    // ------------------------------------------------------
    // Import all required packages and files
    // ------------------------------------------------------

    let Pusher     = require('pusher');
    let express    = require('express');
    let app        = express();
    let bodyParser = require('body-parser')

    let pusher     = new Pusher(require('./config.js'));

    // ------------------------------------------------------
    // Set up Express middlewares
    // ------------------------------------------------------

    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: false }));

    // ------------------------------------------------------
    // Define routes and logic
    // ------------------------------------------------------

    app.post('/status', (req, res, next) => {
      let payload = {username: req.body.username, status: req.body.status};
      pusher.trigger('new_status', 'update', payload);
      res.json({success: 200});
    });

    app.post('/online', (req, res, next) => {
      let payload = {username: req.body.username};
      pusher.trigger('new_status', 'online', payload);
      res.json({success: 200});
    });

    app.get('/', (req, res) => {
      res.json("It works!");
    });


    // ------------------------------------------------------
    // Catch errors
    // ------------------------------------------------------

    app.use((req, res, next) => {
        let err = new Error('Not Found: ');
        err.status = 404;
        next(err);
    });


    // ------------------------------------------------------
    // Start application
    // ------------------------------------------------------

    app.listen(4000, () => console.log('App listening on port 4000!'));

```

In this file, we have created a basic Express application. The application has two important endpoints: `POST /online` and `POST /status`. They both trigger Pusher events with a payload which will be picked up by listeners in our iOS application.

Next create the **config.js** file:

```javascript 
    module.exports = {
        appId: 'PUSHER_ID',
        key: 'PUSHER_KEY',
        secret: 'PUSHER_SECRET',
        cluster: 'PUSHER_CLUSTER',
    };

```

This is our Pusher configuration file. In here, replace the empty strings with the credentials provided in your Pusher dashboard.

Finally, create a **package.json** file:

```javascript 
    {
      "main": "index.js",
      "dependencies": {
        "body-parser": "^1.16.0",
        "express": "^4.14.1",
        "pusher": "^1.5.1"
      }
    }

```

This file contains all the node packages required for the Node app to function properly.

Finally, in the directory of your Node application, run the command below:

```javascript 
    $ npm install && node index.js

```

The first command will install all the dependencies and the second one will start an Express server in node. When you see the message “**_App listening on port 4000!”_** \*\*then you know your backend application is ready.

## Testing our realtime status update application

Once you have your local node web server running, you will need to make some changes so your application can talk to the local web server. In the `info.plist` file, make the following changes:

![](https://blog.pusher.com/wp-content/uploads/2017/10/realtime-status-update-ios-5.png)

With this change, you can build and run your application and it will talk directly with your local web application.

![](https://blog.pusher.com/wp-content/uploads/2017/10/realtime-status-update-ios-1.gif)

## Conclusion

In the article, we have been able to create an iOS application with realtime user status updates, similar to what an application like WhatsApp currently has.

Have a question or feedback on the article? Please ask below in the comment section. The repository for the application and the Node backend is available on [Github](https://github.com/neoighodaro/realtime-ios-status-update).

This post first appeared on the [Pusher blog](https://blog.pusher.com/how-to-build-a-realtime-users-status-update-in-ios/).