Skip to content

Exploring the Chat SDK Architecture

bensmiley edited this page Mar 24, 2017 · 3 revisions

The Chat SDK is designed to be modular and extensible. This is reflected in the architecture of the project.

Chat SDK Architecture

The Chat SDK is divided into four modules:

  1. Core contains interface definitions and utility methods
  2. Chat UI contains all the user interface components
  3. Core Data contains code to store chat data on the device
  4. Network Adapter contains code to communicate with chat server

Each of these modules is contined within it's own CocoaPod. You can install one particular module by adding the subspec to your Podfile.

pod "ChatSDK/Core", :path => "[path to Chat SDK]"
pod "ChatSDK/ChatUI", :path => "[path to Chat SDK]"

Each of these modules is encapsulated using interfaces (called protocols in iOS). You can view all the interfaces used by the app by looking at the Core module. There you will see a folder called Entities and another called Interfaces.

Entities Folder

The entity interfaces define the characteristics that each entity should have. For example, the user entity must have method to set and get the name property.

What this means is that in principle, you could create your own entity classes and as long as they conform to the PUser interface, they will work with the Chat SDK.

Interfaces Folder and Network Adapter

This folder contains the interfaces that are used by the network adapter. The network adapter is a complex class that has multiple roles including sending and recieving messages, updating user profile, user search etc... For this reason, the network adapter is broken up into a number of handler classes.

Chat SDK Architecture

Each handler is responsible for a separate function which is defined by the handler interface. Lets look at the image message handler which is quite simple:

/**
 * @brief Send an image message
 */
-(RXPromise *) sendMessageWithImage: (UIImage *) image withThreadEntityID: (NSString *) threadID;

The image message handler only has one function. In practice, this function performs a number of tasks:

  1. Create a new message entity and add it to the database
  2. Configure the message entity by adding the required properties
  3. Upload the image using the upload handler
  4. Send the message using the core handler

The purpose of this architecture is to make the code easier to read and extend. You could write your own message handler class and test it without modifying any other part of the app.

Equally if you want to see how a piece of functionality works, you can just look at the relevant handler class.

Network Adapter Class

The Network adapter class is a container for all the handlers. It's defined by the BNetworkFacade interface which looks like this:

@protocol BNetworkFacade <NSObject>

// Handlers
-(id<PCoreHandler>) core;
-(id<PAuthenticationHandler>) auth;
-(id<PPushHandler>) push;
-(id<PUploadHandler>) upload;
-(id<PVideoMessageHandler>) videoMessage;
-(id<PAudioMessageHandler>) audioMessage;
-(id<PImageMessageHandler>) imageMessage;
-(id<PLocationMessageHandler>) locationMessage;
-(id<PContactHandler>) contact;
-(id<PTypingIndicatorHandler>) typingIndicator;
-(id<PModerationHandler>) moderation;
-(id<PSearchHandler>) search;
-(id<PPublicThreadHandler>) publicThread;
-(id<PLastOnlineHandler>) lastOnline;
-(id<PBlockingHandler>) blocking;
-(id<PNearbyUsersHandler>) nearbyUsers;
-(id<PReadReceiptHandler>) readReceipt;
-(id<PStickerMessageHandler>) stickerMessage;

@end

For a real life example you can look at the BFirebaseNetworkAdapter class. Here you can see that the network adapter is just a container for the network handlers.

To understand this better, lets look at a practical example.

Example - Adding a last online handler

Currently the Firebase Network Adapter doesn't have the ability to know when a user was last online. However, since the handler exists, it would be easy to add this functionality.

First, you would need to make a new class which conforms to the PLastOnlineHandler interface.

Then you would need to implement the following methods:

-(RXPromise *) getLastOnlineForUser: (id<PUser>) user;
-(RXPromise *) setLastOnlineForUser: (id<PUser>) user;

You could just add a timestamp to the user's Firebase data and retrieve that timestamp.

Now you would need to add your new handler to the Firebase Network adapter. The easiest way to do this would be in the app delegate.

    BFirebaseNetworkAdapter * adapter = [[BFirebaseNetworkAdapter alloc] init];
	adapter.lastOnline = [[YourLastOnlineHandler alloc] init];

Using a handler

Now that you know how to define a handler, it's useful to know how to call a handler from the code.

All the handlers can be accessed through the BNetworkManager singleton. For example, to access the last online handler you would use the following code:

[BNetworkManager sharedManager].a.lastOnline getLastOnlineForUser: ... 

We include the a because this is accessing the network adapter.

To send a message, you would use:

[BNetworkManager sharedManager].a.core sendMessage: (id<PMessage>) message

A good approach to learn the SDK is first to identify the handler you are interested in and then search for it in the code. There you will see how it can be used.

Customizing the user interface

Just as the network code is accessed through a network manager and then a network adapter, the user interface elements can be accessed in a similar way through the interface manager and interface adapter.

So if you wanted to get the private threads view controller you would use the following code:

UIViewController * vc = [BInterfaceManager sharedManager].a privateThreadsViewController];

The reason for this is that it makes it possible to modify the user interface without modifying the framework. If you wanted to create a customized version of the private threads view controller, you would do the following:

  1. Create a new UIViewController class which is a subclass of the BPrivateThreadsViewController class
  2. Create a new class that is a subclass of the BDefaultInterfaceAdapter class.
  3. Override the privateThreadsViewController method and return your new view controller class
  4. In the AppDelegate set the interface adapter to be your class rather than the default class

When you do this, whenever the app requests the private view controller class, it will be given your new private threads class. This allows you to modify the user interface without modifying the framework.