< Back to Blog

Using PubNub for Real-Time Messaging

Using PubNub for Real-Time Messaging

The Challenge

ALFREDO was envisioned as an app that would bring multiple people together for an event (and allow commenting among the participants). In both its iOS and web client implementations, event information must be updated across all friend devices and profiles in real time. If that does not occur, one or more friends may not see the latest event or comment.

From a development standpoint, that requirement posed certain challenges. If one friend adds or changes an event, adds an additional invitee, or even comments on a calendar item, ALFREDO should push that update to everyone. That should happen immediately, too; we did not want to create a solution that would depend upon individuals manually checking for updates.

PubNub

So how best to implement this functionality? From our perspective, building out a real-time messaging system that iOS, the web, and other platforms might use in the future was not an option. There were many existing options we could use to take care of that, and there were other technical challenges we wanted to focus on in developing ALFREDO.

The Solution

The question, though, was which solution to draw on to implement the real-time messaging component. We considered numerous options, but ultimately there was one clear winner: PubNub. PubNub offers a vast library of resources to the development community and from Distillery’s past experiences with PubNub—which we’ve used on a variety of projects—it was a straightforward decision. Implementing PubNub would allow client devices to implement tried and tested libraries and allow our development team to focus on application-level challenges.

PubNub

Implementation

Backend Integration

In developing ALFREDO we used PubNub Ruby V4 SDK 4.0.7 for backend signalling integration. Every time there is a change to a Postgres DB Object (which represents an event, a comment, etc.), the Ruby server publishes a message to all clients subscribed to a channel. By this method, each subscriber is made aware of the change in real time.

ALFREDO takes advantage of PubNub’s Ruby library to publish messages to each friend who needs to see updated information about an event.

def notify_members(data, push_text = nil)
   user_ids = [user_id, commentable.user_id, commentable.organizer_id]
   invitees = commentable.participations
                         .where.not(status: Participation::DECLINED)
                         .where.not(status: Participation::FAILED)
                         .pluck(:user_id)
   user_ids.concat(invitees)

   if push_text.present?
     sns = ApiHelper::Sns.new
     user_ids.uniq.each do |user_id|
       sns.send(user_id, alert: push_text)
     end
   end

   friends = commentable.organizer.friends
   user_ids.concat(friends.members.pluck(:id)) if friends.present?

   user_ids.uniq.each do |user_id|
     PubnubHelper::Publisher.publish(data, user_id)
   end
end

When an event object is changed in the database, a message is pushed to PubNub, which in turn notifies every user subscribed to that channel. In the illustration above, User C is not participating in the event and so does not receive an update.

PubNub architecture

iOS Client Integration

The PubNub iOS SDK 4.4.1 also made it easy to integrate the iOS client. We wanted ALFREDO to communicate with each subscribed iOS device whenever data attached to an event is updated so that the application could reload the data. To accomplish this, we implemented a listener service via PubNub:

@implementation ALFNetworkListenerService
 
- (instancetype)init {
   self = [super init];
   
   if (self) {
       self.pubNubClient = [PubNub clientWithListener:self];
   }
   
   return self;
}
 
- (void)startListeningForChangesWithUserIdentifier:(NSNumber *)userIdentifier {
   [self stopListeningForChanges];
   self.listeningUserIdentifier = userIdentifier;
   [self.pubNubClient subscribeToChannels:@[[NSString stringWithFormat:@"%@%@", 
       AAANetworkListenerChannelPrefix, userIdentifier]] withPresence:YES];
}
 
- (void)stopListeningForChanges {
   [self.pubNubClient unsubscribeFromAll];
   self.listeningUserIdentifier = nil;
   [self.notificationTimer invalidate];
}
 
- (void)client:(PubNub *)client didReceiveMessage:(PNMessageResult *)message {
   DDLogDebug(@"Network Listener Service %@: Did receive update message", self);
   [self scheduleNotificationTimerIfNeeded];
}
#pragma mark - Coalescing
 
- (void)scheduleNotificationTimerIfNeeded {
   if (!self.notificationTimer.isValid) {
       self.notificationTimer = [NSTimer scheduledTimerWithTimeInterval:ALFNotificationCoalescingInterval 
           target:self selector:@selector(notificationTimerFired:) userInfo:nil repeats:NO];
   }
}
 
- (void)notificationTimerFired:(NSTimer *)timer {
   DDLogDebug(@"Network Listener Service %@: Informing delegate of change observation", self);
   if ([self.delegate respondsToSelector:@selector(networkListenerDidObserveChangeForUserIdentifier:)]) {
       [self.delegate networkListenerDidObserveChangeForUserIdentifier:self.listeningUserIdentifier];
   }
}
 
@end

These code snippets enable the following real-time updates to occur — in a manner that is completely transparent to the ALFREDO user:

Calendar Updates

PubNub   PubNub   PubNub

User A — On a screen with calendar event details
User B — Makes a change to that calendar event detail

List Updates

PubNub   PubNub   PubNub

User A — Adds an item to the a list
User B — Sees that added item

Comment Update

PubNub   PubNub   PubNub

User A — Comments on an event
User B — Sees that comment

PubNub architecture

Web Client Integration

Finally, we are enabling web client integration using PubNub Angular JS, which extends the JS library from PubNub so that subscribers using the ALFREDO web client can experience the same real-time updates experienced by subscribers using the iOS application. This allows users to move back and forth between the iOS and web client versions (as they might when moving between a mobile and desktop-based interaction with the app) and know that the schedule and event information they see is always up-to-date.

A Javascript file enables the web application to subscribe to user channels so that it can be propagated through the web app.

(function () {
    'use strict';

    angular.module('pubnubService', [])
        .factory('PubnubService', PubnubService);

    PubnubService.$inject = [
        '$state',
        '$log',
        'UserRemoteService',
        'Pubnub',
        'appConfig',
        '$rootScope',
        'UserService'
    ];

    function PubnubService($state, $log, UserRemoteService, Pubnub, appConfig, $rootScope, UserService) {
        var userId = null;

        return {
            subscribe: function () {
                if (UserService.isLogged()) {
                    UserRemoteService.getCurrent(function (user) {
                        userId = user.id;
                        var channel = appConfig.PUBNUB_PREFIX + userId;
                        Pubnub.subscribe({
                            channel: channel,
                            callback: function (data) {
                                $log.log('PubNub update: ', data);
                                var isAddEvent = $state.includes('index.addEvent');
                                var isEditEvent = $state.includes('index.editEvent');

                                if (!$rootScope.isLocalRequest) {
                                    $rootScope.themeColor = UserService.get().color;
                                    if (!isAddEvent && !isEditEvent) {
                                        $state.reload();
                                    }
                                }
                                $rootScope.isLocalRequest = false;
                            }
                        });
                    });
                }
            },
            unsubscribe: function () {
                Pubnub.unsubscribe({
                    channel: appConfig.PUBNUB_PREFIX + userId
                });
            }
        };
    }

})();

Summary

By using PubNub to facilitate real-time messaging, we are able to provide an infrastructure for the ALFREDO application that allows users to “magically” see all event and schedule updates in real time — without having to perform any kind of manual updates (i.e., “Pull to Refresh”). Building that infrastructure ourselves would have been time-consuming and superfluous, for PubNub has already done a great job of building the libraries and delivering the functionality we required.

What’s Next?

The initial ALFREDO implementation leverages the PubNub platform for signalling. We anticipate taking advantage of other PubNub features as we bring additional functionality to ALFREDO.

Adding Data to the Published Messages

In its initial implementation, ALFREDO handles updates by sending a message to a client that an event has been updated. The client app responds by sending a message requesting the updated information, which it then uses to update its local data store. Using other features of PubNub’s publish-and-subscribe mechanism, we can add data to the message published from the backend, and ALFREDO will be able to pass updated event or conversation data that the client device can ingest directly. This will eliminate the need to have the client device request the updated data from the server. PubNub enables developers to encrypt the body of published messages, so we will be able to push updates to the clients in a secure manner.

Subscribing Clients to Channel Groups

Currently the application is subscribed to a single channel (userid); in future versions the application can subscribe to multiple channels enabling group communication. For example, all users could be subscribed to the “friends” channel for updates that would be needed for all members. This is a path to discrete group messaging for the ALFREDO application.

Converting Commenting System to Real-Time Chat

With the commenting system in place, we have the framework for the evolution of a real-time chat application that is tied to specific events/channels. Implementing real-time chat will require some changes to the current framework, but we expect that deploying such a chat system using PubNub’s libraries will be straightforward.

Can’t wait to integrate PubNub’s technologies into your app? Distillery is an official certified PubNub Partner and will gladly help you with that. Just let us know!

Distillery is an official PubNub Certified Partner

 

Nabi Makhmudov

About the Author

Nabi Makhmudov has been Distillery’s lead iOS developer for many years. His swiftness and objectivity are legendary among his colleagues. In 2016, he permanently relocated to Santa Monica so that he can stay as close as possible to the latest emerging technologies. Beyond creating high-towered castles of extremely high-quality code, Nabi is fond of classic music and science.


BACK TO TOP >