Listening to a server heartbeat using Node.js, iOS, and Socket.IO (Part 1)

Introduction

This two-part blog post will cover setting up and connecting to a server that's sending out a "heartbeat" at a regular interval. This is a common functionality when a client relies on up-to-date information.

This example will cover cycling through a list of four colors, in sychronization with all connected iOS devices.

Part 1 will cover the iOS side. For now, a sample server is available.

Setup

This part of the tutorial will use the following tools:

  • Xcode 5
  • iOS 7

Diving In

First, start by opening Xcode and creating a new project with the Single View Application template and name it Heartbeat.

Hearbeat Image 1

Hearbeat Image 2

We'll be using a protocol called Socket.IO. The official implementation for javascript is available here, but the iOS implementation is a bit different. We'll use a library called socket.IO-objc. The current version on Github has given me problems, so I've uploaded a slightly modified version.

Download my version of socket.IO-objc from here.

Unzip the library, then drag the folder into Xcode.

Hearbeat Image 3

Be sure that "Copy items into destination . . ." is checked!, then press finish.

Hearbeat Image 4

The last step for importing the framework is to add its dependencies. socket.IO-objc depends on:

  • libicucore.dylib
  • CFNetwork.framework
  • Security.framework

Hearbeat Image 5

Now, in the ViewController.h file, we need to import the SocketIO class, add a property for a SocketIO object, and implement the SocketIODelegate protocol.

The SocketIODelegate protocol will allow the ViewController to intercept SocketIO notifications.

After this, ViewController.h should look like this:

/*
 * Copyright Stuff
 */

#import <UIKit/UIKit.h>
#import "SocketIO.h"

@interface ViewController : UIViewController <SocketIODelegate>

@property (strong, nonatomic) SocketIO *socketConnection;

@end

Next, in ViewController.m, we'll set up the socketConnection object, implement the delegate method, and listen for messages.

First, in -viewDidLoad:

- (void)viewDidLoad
{
    [super viewDidLoad];
    _socketConnection = [[SocketIO alloc] initWithDelegate:self];
    [_socketConnection connectToHost:@"sch.kylelevin.com" onPort:80 withParams:nil withNamespace:@"/ios"];
}

First, we create a new SocketIO object and assign the delegate to the ViewController.
Then, we connect to sch.kylelevin.com under the namespace /ios.

We'll cover the namespace in Part 2 of this tutorial, but for now, just understand that the server will send a message to all connections under the /ios namespace every second.

Now that we've written the connection code, we'll implement two more methods from the SocketIODelegate.

- (void)socketIODidConnect:(SocketIO *)socket
{
    NSLog(@"Socket has connected!");
}

and

- (void)socketIO:(SocketIO *)socket didReceiveEvent:(SocketIOPacket *)packet
{
    NSLog(@"%@", packet.dataAsJSON);
}

The first method will be called when the socket successfully connects.

The second method is called every time the socket receives an "event" from the server, then spits out the data to the console using NSLog().

At this point, the ViewController.m should look something like this:

Hearbeat Image 6

If we run the project now, the console log should start populating with events.

Hearbeat Image 7

You can see that every second, the iOS device receives a message named updateColor, with a counter from 0 to 3.

Since we know there are only four possible numbers, we can parse these messages and change, for example, the background color.

We need to pull out the "args" value from the SocketIO event.

We can do this by modifying the method to look like this:

- (void)socketIO:(SocketIO *)socket didReceiveEvent:(SocketIOPacket *)packet
{
    // Grab the data from the packet.
    NSDictionary *dict = packet.dataAsJSON;

    // Pull out the arguments from the dictionary.
    NSArray *args = dict[@"args"];

    // Access the number object at index 0 of the "args" array.
    NSNumber *numberObject = args[0];

    // Convert the number object into a standard integer.
    int number = [numberObject intValue];

    // %d is a placeholder for a variable of type "int".
    NSLog(@"%d", number);
}

Running the app again, the output will look much cleaner:

Hearbeat Image 8

Now that we have extracted the counter from the Socket.IO event, we need to create a method to handle changing the background color based on that number.

Below the -socketIO:didReceiveEvent: method,

- (void)changeBackgroundColorWithCount:(int)count
{
    // Declare an empty color object.
    UIColor *color;
    if(count == 0)
    {
        color = [UIColor blueColor];
    }
    else if(count == 1)
    {
        color = [UIColor redColor];
    }
    else if(count == 2)
    {
        color = [UIColor orangeColor];
    }
    else if(count == 3)
    {
        color = [UIColor purpleColor];
    }
    else
    {
        //If we get here, then the method doesn't recognize the "count" input
        color = [UIColor whiteColor];
    }

    self.view.backgroundColor = color;
}

And finally, in the -socketIO:didReceiveEvent: method, add the following to the end:

// Change the background color with the message's number as input.
[self changeBackgroundColorWithCount:number];

Conclusion

At this point, running the app should have the background color cycling through the list of colors determined in the -changeBackgroundColorWithCount: method we just created. In part 2, I'll demonstrate how to setup the server-side portion of this, rather than relying on my personal server.

I've uploaded all of the code from this tutorial here as a reference in the event of any troubles.