SimpleSample-chat users-ios

Ask tech team
From QuickBlox Developers (API docs, code samples, SDK)
Jump to: navigation, search

Contents

Sources

Project homepage on GIT:


Clone source code via Terminal (the way we recommend):

git clone git@github.com:QuickBlox/quickblox-ios-sdk.git

or download ZIP:


Additional components

  • QMChatViewController - an elegant ready-to-go chat view controller for iOS chat applications that use QuickBlox communication backend.
  • QMServices - easy-to-use services for Quickblox SDK, for speeding up development of iOS chat applications


Overview

This sample demonstrates how to work with QuickBlox Chat API.
It allows you to organise group Chat & 1 to 1 chat

It gives examples of how to:

  1. Organise chat between 2 users
  2. Organise group chat


Guide: Getting Started with Chat API

Get a QuickBlox account

http://admin.quickblox.com/register

Create applications in the Admin panel

http://admin.quickblox.com/apps/new

In addition, there is this helpful 5 minute guide.

Connect QuickBlox to your application

To get the information on how to connect to the QuickBlox.framework, please, refer to the IOS-how-to-connect-Quickblox-framework page.

Connect to Chat

Note: In order to connect to the chat please read the information about Chat login/password formation.

QBUUser *currentUser = [QBUUser user];
currentUser.ID = 56
currentUser.password = @"chatUser1pass";
 
// connect to Chat
[[QBChat instance] connectWithUser:user completion:^(NSError * _Nullable error) {
 
}];
let user = QBUUser()
user.ID = 56
user.password = "chatUser1pass"
 
QBChat.instance().connectWithUser(user) { (error: NSError?) -> Void in
 
}

Custom resource

Is it possible to set custom resource for chat connect to identify current device.

[[QBChat instance] connectWithUser:user resource:@"iPhone6s" completion:^(NSError * _Nullable error) {
 
}];
QBChat.instance().connectWithUser(user, resource: "iPhone6") { (error: NSError?) -> Void in
 
}

By default we use [[[UIDevice currentDevice] identifierForVendor] UUIDString] as a chat resource.

Secure connection

By default Chat works over TLS connection.

Chat in background mode

As iOS doesn't provide 'true' background mode, we can't have a persistent Chat connection while iOS application is in background. The better way to handle correctly chat offline messages is to do 'Chat disconnect' when app goes to background and does 'Chat connect' when app goes to foreground:

@implementation AppDelegate
 
// this is our AppDelegate class
 
- (void)applicationWillTerminate:(UIApplication *)application
{
   [[QBChat instance] disconnectWithCompletionBlock:^(NSError * _Nullable error) {
 
   }];
}
 
- (void)applicationDidEnterBackground:(UIApplication *)application
{
   [[QBChat instance] disconnectWithCompletionBlock:^(NSError * _Nullable error) {
 
   }];
}
 
- (void)applicationWillEnterForeground:(UIApplication *)application
{
    [[QBChat instance] connectWithUser:user  completion:^(NSError * _Nullable error) {
 
    }];
}
class AppDelegate: UIResponder, UIApplicationDelegate {
 
    func applicationDidEnterBackground(application: UIApplication) {
	QBChat.instance().disconnectWithCompletionBlock { (error: NSError?) -> Void in
 
        }	
    }
 
    func applicationWillTerminate(application: UIApplication) {
	QBChat.instance().disconnectWithCompletionBlock { (error: NSError?) -> Void in
 
        }
    }
 
    func applicationWillEnterForeground(application: UIApplication) { 
	QBChat.instance().connectWithUser(user) { (error: NSError?) -> Void in
 
        }
    }
}


Disconnect from Chat

Next code does chat disconnect:

if([[QBChat instance] isConnected]){
    [[QBChat instance] disconnectWithCompletionBlock:^(NSError * _Nullable error) {
 
    }];
}
if QBChat.instance().isConnected() {
    QBChat.instance().disconnectWithCompletionBlock { (error: NSError?) -> Void in
 
    }
}


Keep alive settings

Many routers will teardown a socket mapping if there is no activity on the socket. For this reason, the stream supports sending keep-alive data. Keep-alive data is only sent in the absence of any other data being sent/received. The default value is 20s. The minimum value is 10s. To disable keep-alive, set the interval to zero (or any non-positive number).

[QBSettings setKeepAliveInterval:30];
QBSettings.setKeepAliveInterval(30);


Reconnection

By default iOS SDK doesn't reconnect automatically when connection to server is lost. You have to manage this manually.

But there is a way to enable automatic reconnection:

[QBSettings setAutoReconnectEnabled:YES];
 QBSettings.setAutoReconnectEnabled(true);

There are also QBChatDelegate methods to track the reconnection process state:

- (void)chatDidAccidentallyDisconnect{
 
}
 
- (void)chatDidReconnect{
 
}
func chatDidAccidentallyDisconnect() {
 
}
 
func chatDidReconnect() {
 
}

Dialogs

QuickBlox provides a way to organise a chat dialogs page where you can show all user's current chat dialogs.

Dialogs can have next types:

  • 1-1 private dialog - a chat dialog between 2 users
  • Group dialog - a chat dialog between many users
  • Open group dialog - open chat dialog, any user can join and chat here.

Retrieve list of dialogs

Here is how typical screen with all your chats looks like:

Chat2.0 chatslist.png

Request Dialogs each time user logins to app:

// NSMutableDictionary *extendedRequest = @{@"sort_desc" : @"_id"}.mutableCopy;
 
QBResponsePage *page = [QBResponsePage responsePageWithLimit:100 skip:0];
 
[QBRequest dialogsForPage:page extendedRequest:nil successBlock:^(QBResponse *response, NSArray *dialogObjects, NSSet *dialogsUsersIDs, QBResponsePage *page) {
 
} errorBlock:^(QBResponse *response) {
 
}];
let extendedRequest = ["sort_desc" : "_id"]
 
let page = QBResponsePage(limit: 100, skip: 0)
 
QBRequest.dialogsForPage(page, extendedRequest: extendedRequest, successBlock: { (response: QBResponse, dialogs: [QBChatDialog]?, dialogsUsersIDs: Set<NSNumber>?, page: QBResponsePage?) -> Void in
 
    }) { (response: QBResponse) -> Void in
 
}

Filters

There are some filters to get only chat dialogs you need, not just all.


You can apply filters for the following fields:

  • _id (string)
  • type (integer)
  • name (string)
  • last_message_date_sent (integer)
  • created_at (date)
  • updated_at (date)

You can apply sort for the following fields:

  • last_message_date_sent


Filters:

Operator Description Usage example
{field_name} Search records with field which contains exactly specified value extendedRequest[@"type"] = @(2);
{field_name}[{search_operator}] Search record with field which contains value according to specified value and operator.

Possible filters: lt (Less Than operator), lte (Less Than or Equal to operator), gt (Greater Than operator), gte (Greater Than or Equal to operator), ne (Not Equal to operator), in (Contained IN array operator), nin (Not contained IN array), all (ALL contained IN array), or (OR operator), ctn (Contains substring operator)
extendedRequest[@"type[in]"] = @"1,2";
limit Limit search results to N records. Useful for pagination. Default value - 100 extendedRequest[@"limit"] = @(50);
skip Skip N records in search results. Useful for pagination. Default (if not specified): 0 extendedRequest[@"skip"] = @(50);
sort_desc/sort_asc Search results will be sorted by specified field in ascending/descending order extendedRequest[@"sort_desc"] = @"last_message_date_sent";

Get a number of dialogs

There is a separate request to get a number of dialogs:

[QBRequest countOfDialogsWithExtendedRequest:nil successBlock:^(QBResponse *response, NSUInteger count) {
    // success
} errorBlock:^(QBResponse *response) {
    // error
}];
QBRequest.countOfDialogsWithExtendedRequest(nil, successBlock: { (response : QBResponse!, count : UInt) -> Void in
 
}) { (response : QBResponse!) -> Void in
 
}

Create new dialog

According to REST API, Create a dialog query, you don't need to create a dialog for 1-1(private) chat - it will be created automatically with 1st chat message. But you can create it anyway if you need this for your application.

Create new group chat dialog

When user would like to start a group chat - he should create a group chat dialog first.

Chat2.0 chatnew.png

User should choose other users to add them to a new group dialog. Also he must choose a name for new group chat:

QBChatDialog *chatDialog = [[QBChatDialog alloc] initWithDialogID:null type:QBChatDialogTypeGroup];
chatDialog.name = @"Chat with Bob, Sam, Garry";
chatDialog.occupantIDs = @[@(55), @(678), @(22)];
 
[QBRequest createDialog:chatDialog successBlock:^(QBResponse *response, QBChatDialog *createdDialog) {
 
} errorBlock:^(QBResponse *response) {
 
}];
let chatDialog = QBChatDialog(dialogID: nil, type: QBChatDialogType.Group)
chatDialog.name = "Chat with Bob, Sam, Garry"
chatDialog.occupantIDs = [55, 678, 22]
 
QBRequest.createDialog(chatDialog, successBlock: { (response: QBResponse?, createdDialog : QBChatDialog?) -> Void in
 
}) { (responce : QBResponse!) -> Void in
 
}

To notify all occupants that you created a group chat we use chat notifications - it's simple chat message with extra parameters inside. These parameters used to separate chat notifications from regular text chat messages:

- (QBChatMessage *)createChatNotificationForGroupChatCreation:(QBDialog *)dialog
{
    // create message:
    QBChatMessage *inviteMessage = [QBChatMessage message];
 
    NSMutableDictionary *customParams = [NSMutableDictionary new];
    customParams[@"xmpp_room_jid"] = dialog.roomJID;
    customParams[@"name"] = dialog.name;
    customParams[@"_id"] = dialog.ID;
    customParams[@"type"] = @(dialog.type);
    customParams[@"occupants_ids"] = [dialog.occupantIDs componentsJoinedByString:@","];
 
    // Add notification_type=1 to extra params when you created a group chat 
    //
    customParams[@"notification_type"] = @"1";
 
    inviteMessage.customParameters = customParams;
 
    return inviteMessage;
}
 
...
 
for (NSString *occupantID in dialog.occupantIDs) {
 
    QBChatMessage *inviteMessage = [self createChatNotificationForGroupChatCreation:dialog];
 
    NSTimeInterval timestamp = (unsigned long)[[NSDate date] timeIntervalSince1970];
    customParams[@"date_sent"] = @(timestamp);
 
    // send notification
    //
    inviteMessage.recipientID = [occupantID integerValue];
 
    [[QBChat instance] sendSystemMessage:inviteMessage completion:^(NSError * _Nullable error) {
 
    }];
}
func createChatNotificationForGroupChatCreation(dialog: QBChatDialog) -> QBChatMessage {
    var inviteMessage: QBChatMessage = QBChatMessage()
    var customParams: NSMutableDictionary = NSMutableDictionary()
    customParams["xmpp_room_jid"] = dialog.roomJID
    customParams["name"] = dialog.name
    customParams["_id"] = dialog.ID
    customParams["type"] = dialog.type.rawValue
    customParams["occupants_ids"] = ", ".join(dialog.occupantIDs as! [String])
    customParams["notification_type"] = "1"
    inviteMessage.customParameters = customParams
    return inviteMessage
}
 
...
 
for occupantID in dialog.occupantIDs {
    var inviteMessage: QBChatMessage = self.createChatNotificationForGroupChatCreation(dialog)
    var timestamp: NSTimeInterval = NSDate().timeIntervalSince1970
    customParams["date_sent"] = timestamp
    inviteMessage.recipientID = occupantID.unsignedLongValue
 
    QBChat.instance().sendSystemMessage(inviteMessage) { (error: NSError?) -> Void in
 
    }
}


All opponents will receive this in delegate:

- (void)chatDidReceiveSystemMessage:(QBChatMessage *)message
{
}
func chatDidReceiveSystemMessage(message: QBChatMessage!) {
 
}


Set dialog's avatar

The Chat dialog contains a field photo. It's a string field, can contain any value:

  • An ID of a file in Content module: iOS example. This is a way we recommend.
  • An ID of a file in Custom Objects module: iOS example
  • Can be an url to any file in Internet


For example, we use Content module to store the dialog's photo. Next snippets show how to upload a file to Content module and set it as a photo of a dialog:

// Upload a file to the Content module
NSData *imageData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"image45" ofType:@"png"]];
 
[QBRequest TUploadFile:imageData fileName:@"arrow.png" contentType:@"image/png" isPublic:NO successBlock:^(QBResponse *response, QBCBlob *uploadedBlob) {
    // set dialog's photo
    NSUInteger uploadedFileID = uploadedBlob.ID;
    dialog.photo = [NSString stringWithFormat:@"%d", uploadedFileID];
} statusBlock:^(QBRequest *request, QBRequestStatus *status) {
    // handle progress            
} errorBlock:^(QBResponse *response) {
    NSLog(@"error: %@", response.error);
}];
var imageData : NSData! = NSData(contentsOfFile : NSBundle.mainBundle().pathForResource("image45", ofType: "png")!)
 
QBRequest.TUploadFile(imageData, fileName: "arrow.png", contentType: "image/png", isPublic: false, successBlock: { (response : QBResponse!, uploadedBlob :QBCBlob!) -> Void in
    var uploadedFileID: UInt = uploadedBlob.ID
    dialog.photo = String(uploadedFileID)
 
    }, statusBlock: { (request : QBRequest?, status : QBRequestStatus?) -> Void in
 
    }) { (response : QBResponse!) -> Void in
}

Create new 1-1(private) chat dialog

If you would like to create a private chat dialog too - use next snippets:

QBChatDialog *chatDialog = [[QBChatDialog alloc] initWithDialogID:null type:QBChatDialogTypePrivate];
chatDialog.occupantIDs = @[@(1530190)];
 
[QBRequest createDialog:chatDialog successBlock:^(QBResponse *response, QBChatDialog *createdDialog) {
 
} errorBlock:^(QBResponse *response) {
 
}];
var chatDialog: QBChatDialog = QBChatDialog(dialogID: nil, type: QBChatDialogType.Private)
chatDialog.occupantIDs = [1530190]
 
QBRequest.createDialog(chatDialog, successBlock: {(response: QBResponse?, createdDialog: QBChatDialog?) in
 
}, errorBlock: {(response: QBResponse!) in
 
})

Custom parameters

Dialogs can store additional parameters. These parameters can be used to store an additional data. Also these parameters can be used in dialogs retrieval requests.

To start use additional parameters it needs to create an additional schema of your parameters. This is a CustomObjects class. Just create an empty class with all needed fields. These fields will be your dialog additional parameters.

Next, to set additional parameters to a dialog use next additional parameters in a creation request:

  1. data[class_name] - should contain CustomObjects class name created above
  2. data[field1]
  3. data[...]
  4. data[fieldN]


To create a dialog with custom parameters set a dialog's field data:

newDialog.data = @{@"class_name": @"dialog_data", @"age": @33};
dialog.data = ["class_name": "dialog_data", "age": 33];


It's also possible to use custom parameters in a dialogs retrieval requests so dialogs can be filtered through custom parameters:

NSMutableDictionary *extRequest = @{@"data[class_name]" : @"dialog_data", @"data[age]": @33}.mutableCopy;
let extRequest = ["data[class_name]" : "dialog_data", "data[age]" : 33]

Update group dialog

The following rules are applied if dialog's type is 2(GROUP):

  • Any user from occupants_ids can add other users.
  • Any user from occupants_ids can remove only himself.
  • Only dialog's creator(owner) can remove any users from occupants_ids.

The following rules are applied if dialog's type is 1(PUBLIC_GROUP):

  • Only dialog’s owner can update it.

User can update group chat dialog name, add new occupants or leave this group chat. To add more occupants use push operator. To leave group chat (remove yourself) - use pull operator:

Chat2.0 groupchatdetails.png


QBChatDialog *updateDialog = [[QBChatDialog alloc] initWithDialogID:@"53aac645535c12bd3b008a40" type:QBChatDialogTypeGroup];
updateDialog.pushOccupantsIDs = @[@"300", @"301", @"302"];
updateDialog.name = @"school friends";
 
[QBRequest updateDialog:updateDialog successBlock:^(QBResponse *responce, QBChatDialog *dialog) {
 
} errorBlock:^(QBResponse *response) {
 
}];
var updateDialog = QBChatDialog(dialogID: "53aac645535c12bd3b008a40", type: QBChatDialogType.Group)
updateDialog.setPushOccupantsIDs(["300", "301", "302"])
updateDialog.name = "school friends"
QBRequest.updateDialog(updateDialog, successBlock: {(responce: QBResponse?, dialog: QBChatDialog?) in
 
}, errorBlock: {(response: QBResponse!) in
 
})

To notify all occupants that you updated a group chat we use chat notifications - it's simple chat message with extra parameters inside. These parameters used to separate chat notifications from regular text chat messages:

- (QBChatMessage *)createChatNotificationForGroupChatUpdate:(QBDialog *)dialog
{
    // create message:
    QBChatMessage *inviteMessage = [QBChatMessage message];
    inviteMessage.text = @"optional text";
 
    NSMutableDictionary *customParams = [NSMutableDictionary new];
    customParams[@"xmpp_room_jid"] = dialog.roomJID;
    customParams[@"name"] = dialog.name;
    customParams[@"_id"] = dialog.ID;
    customParams[@"type"] = @(dialog.type);
    customParams[@"occupants_ids"] = [dialog.occupantIDs componentsJoinedByString:@","];
 
    // Add notification_type=2 to extra params when you updated a group chat 
    //
    customParams[@"notification_type"] = @"2";
 
    inviteMessage.customParameters = customParams;
 
    return inviteMessage;
}
 
...
 
for (NSString *occupantID in dialog.occupantIDs) {
 
    QBChatMessage *inviteMessage = [self createChatNotificationForGroupChatUpdate:dialog];
 
    NSTimeInterval timestamp = (unsigned long)[[NSDate date] timeIntervalSince1970];
    customParams[@"date_sent"] = @(timestamp);
 
    // send notification
    //
    inviteMessage.recipientID = [occupantID integerValue];
 
    [[QBChat instance] sendSystemMessage:message completion:^(NSError * _Nullable error) {
 
    }];
}
func createChatNotificationForGroupChatUpdate(dialog: QBChatDialog) -> QBChatMessage {
    var inviteMessage: QBChatMessage = QBChatMessage()
 
    inviteMessage.text = "optional text"
 
    var customParams: NSMutableDictionary = NSMutableDictionary()
    customParams["xmpp_room_jid"] = dialog.roomJID
    customParams["name"] = dialog.name
    customParams["_id"] = dialog.ID
    customParams["type"] = dialog.type.rawValue
    customParams["occupants_ids"] = ", ".join(dialog.occupantIDs as! [String])
    customParams["notification_type"] = "2"
    inviteMessage.customParameters = customParams
    return inviteMessage
}
 
...
 
for occupantID in dialog.occupantIDs {
    var inviteMessage: QBChatMessage = self.createChatNotificationForGroupChatUpdate(dialog)
    var timestamp: NSTimeInterval = NSDate().timeIntervalSince1970
    customParams["date_sent"] = timestamp
    inviteMessage.recipientID = occupantID.unsignedLongValue
 
    QBChat.instance().sendSystemMessage(inviteMessage) { (error: NSError?) -> Void in
 
    }
}

All opponents will receive this in delegate:

- (void)chatDidReceiveSystemMessage:(QBChatMessage *)message
{
}
func chatDidReceiveSystemMessage(message: QBChatMessage!) {
 
}

Delete dialog

To delete a dialog use next snippet:

[QBRequest deleteDialogsWithIDs:[NSSet setWithObject:@"560a7dd3a0eb471d3d0020a1"] forAllUsers:NO
                   successBlock:^(QBResponse *response, NSArray *deletedObjectsIDs, NSArray *notFoundObjectsIDs, NSArray *wrongPermissionsObjectsIDs) {
 
} errorBlock:^(QBResponse *response) {
 
}];
QBRequest.deleteDialogsWithIDs(Set(arrayLiteral: "560a7dd3a0eb471d3d0020a1"), forAllUsers: false, 
    successBlock: { (response: QBResponse!, deletedObjectsIDs: [String]?, notFoundObjectsIDs: [String]?, wrongPermissionsObjectsIDs: [String]?) -> Void in
 
}) { (response: QBResponse!) -> Void in
 
}

NOTE: If you pass YES to the 'forAllUsers' parameter and you are the owner of the dialog, than dialog will be deleted for ALL users. If you pass NO to the 'forAllUsers' parameter, than it will be deleted only for current user and other users occupants of the dialog will still have it.

Chat in dialog

Chat in 1-1 private dialog

To send a message in 1-1 chat use the QBChatDialog instance:

[[QBChat instance] addDelegate:self];
 
...
 
QBChatDialog *privateChatDialog = ...;
 
QBChatMessage *message = [QBChatMessage message];
[message setText:@"Hey there"];
//
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"save_to_history"] = @YES;
[message setCustomParameters:params];
//
[privateChatDialog sendMessage:message completionBlock:^(NSError * _Nullable error) {
 
}];
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
- (void)chatDidReceiveMessage:(QBChatMessage *)message{
 
}
QBChat.instance().addDelegate(self)
 
let privateChatDialog: QBChatDialog = ...
let message: QBChatMessage = QBChatMessage()
message.text = "Hey there"
 
let params : NSMutableDictionary = NSMutableDictionary()
params["save_to_history"] = true
message.customParameters = params
 
privateChatDialog.sendMessage(message, completionBlock: { (error: NSError?) -> Void in
 
});
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
func chatDidReceiveMessage(message: QBChatMessage!) {
 
}

Chat in group dialog

Before start chating in a group dialog you should join this dialog.

QBChatDialog *groupChatDialog = ...;
[groupChatDialog joinWithCompletionBlock:^(NSError * _Nullable error) {
 
}];
var groupChatDialog: QBChatDialog = ...
groupChatDialog.joinWithCompletionBlock { (error: NSError?) -> Void in
 
}

After join you can send and receive messages:

QBChatDialog *groupChatDialog = ...;
 
QBChatMessage *message = [QBChatMessage message];
[message setText:@"Hey there"];
//
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"save_to_history"] = @YES;
[message setCustomParameters:params];
//
[groupChatDialog sendMessage:message completionBlock:^(NSError * _Nullable error) {
 
}];
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
- (void)chatRoomDidReceiveMessage:(QBChatMessage *)message fromDialogId:(NSString *)dialogId{
 
}
var groupChatDialog: QBChatDialog = ...
var message: QBChatMessage = QBChatMessage()
message.text = "Hey there"
var params = NSMutableDictionary()
params["save_to_history"] = true
message.customParameters = params
 
groupChatDialog.sendMessage(message, completionBlock: { (error: NSError?) -> Void in
 
});
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
func chatRoomDidReceiveMessage(message: QBChatMessage!, fromDialogID dialogID: String!) {
 
}
Get online users

You can request online users in group chat dialog if you are in the list of occupants.

You should be connected to chat to use this method.

QBChatDialog *groupChatDialog = ...;
 
[groupChatDialog requestOnlineUsersWithCompletionBlock:^(NSMutableArray<NSNumber *> * _Nullable onlineUsers, NSError * _Nullable error) {
 
}];
var groupChatDialog: QBChatDialog = ...
groupChatDialog.requestOnlineUsersWithCompletionBlock { (users: NSMutableArray?, error: NSError?) -> Void in
 
}

And you also can handle online users in real time:

QBChatDialog* createdDialog = [[QBChatDialog alloc] initWithDialogID:@"55b0feb95b31459c762e112c" type:QBChatDialogTypeGroup];
 
[createdDialog setOnJoinOccupant:^(NSUInteger userID) {
 
}];
 
[createdDialog setOnLeaveOccupant:^(NSUInteger userID) {
 
}];
 
[createdDialog setOnUpdateOccupant:^(NSUInteger userID) {
 
}];
var createdDialog: QBChatDialog = QBChatDialog(dialogID: "55b0feb95b31459c762e112c", type: QBChatDialogType.Group)
 
createdDialog.onJoinOccupant = {(userID: UInt) in
}
 
createdDialog.onLeaveOccupant = {(userID: UInt) in
}
 
createdDialog.onUpdateOccupant = {(userID: UInt) in
}
Leave group chat dialog
QBChatDialog *groupChatDialog = ...;
[groupChatDialog leaveWithCompletionBlock:^(NSError * _Nullable error) {
 
}];
var groupChatDialog: QBChatDialog = ...;
groupChatDialog.leaveWithCompletionBlock { (error: NSError?) -> Void in
 
}

Send and receive a message with attachment

Send attachment

It's possible to add attachments to message: for example, image, audio file or video file. We don't have any restrictions here - you can attach any type of file.

To send a message with attachments you should use the same way as you send regular message with text, but add to it an attachment object. Attachment can be:

  • A file in Content module: iOS example. This is a way we recommend.
  • A file in Custom Objects module: iOS example
  • Can be an url to any file in Internet


To send a message with attachment you should upload a file to Content module, Custom Objects module using sample above or use an url to any file in Internet. Then you should incorporate an ID to file to message.

For example, we use Content module to store attachments. Next snippets show how to upload a file to Content module and send it as an attach:

// Upload a file to the Content module
NSData *imageData = UIImagePNGRepresentation([UIImage imageNamed:@"arrow.png"]);
 
[QBRequest TUploadFile:imageData fileName:@"arrow.png" contentType:@"image/png" isPublic:NO successBlock:^(QBResponse *response, QBCBlob *uploadedBlob) {
    NSUInteger uploadedFileID = uploadedBlob.ID;
 
    // Create chat message with attach
    //
    QBChatMessage *message = [QBChatMessage message];
 
    ...
 
    QBChatAttachment *attachment = QBChatAttachment.new;
    attachment.type = @"image";
    attachment.ID = [NSString stringWithFormat:@"%d", uploadedFileID]; // use 'ID' property to store an ID of a file in Content or CustomObjects modules
    [message setAttachments:@[attachment]];
} statusBlock:^(QBRequest *request, QBRequestStatus *status) {
    // handle progress            
} errorBlock:^(QBResponse *response) {
    NSLog(@"error: %@", response.error);
}];
var imageData: NSData = UIImagePNGRepresentation(UIImage(named: "arrow.png")!)!
QBRequest.TUploadFile(imageData, fileName: "arrow.png", contentType: "image/png", isPublic: false, successBlock: {(response: QBResponse!, uploadedBlob: QBCBlob!) in
    // Create and configure message
    var message: QBChatMessage = QBChatMessage()
 
    var uploadedFileID: UInt = uploadedBlob.ID
    var attachment: QBChatAttachment = QBChatAttachment()
    attachment.type = "image"
    attachment.ID = String(uploadedFileID)
    message.attachments = [attachment]
    // Send message
    }, statusBlock: {(request: QBRequest?, status: QBRequestStatus?) in
 
    }, errorBlock: {(response: QBResponse!) in
        NSLog("error: %@", response.error)
})

Receive attachment

For example we use Content module to store attachments. Next snippets allow to receive a message with an attachment and download it:

#pragma mark -
#pragma mark QBChatDelegate
 
- (void)chatDidReceiveMessage:(QBChatMessage *)message{
    for(QBChatAttachment *attachment in message.attachments){
        // download file by ID
        [QBRequest downloadFileWithID:[attachment.ID integerValue] successBlock:^(QBResponse *response, NSData *fileData) {
            UIImage *image = [UIImage imageWithData:fileData];
 
        } statusBlock:^(QBRequest *request, QBRequestStatus *status) {
            // handle progress            
        } errorBlock:^(QBResponse *response) {
            NSLog(@"error: %@", response.error);
        }];
    }
}
// MARK QBChatDelegate
 
func chatDidReceiveMessage(message: QBChatMessage) {
    for attachment : QBChatAttachment in message.attachments as! [QBChatAttachment] {
        QBRequest.downloadFileWithID(UInt(attachment.ID.toInt()!), successBlock: {(response: QBResponse!, fileData: NSData!) in            var image: UIImage? = UIImage(data: fileData)
 
            }, statusBlock: {(request: QBRequest!, status: QBRequestStatus!) in
 
            }, errorBlock: {(response: QBResponse!) in
                NSLog("error: %@", response.error)
        })
    }
}

It's also possible to obtain a link to attachment and use to show an image:

- (void)chatDidReceiveMessage:(QBChatMessage *)message{
    for(QBChatAttachment *attachment in message.attachments){
        // or if you have only file ID
        NSString *privateUrl = [QBCBlob privateUrlForID:[attachment.ID integerValue]];
    }
}
func chatDidReceiveMessage(message: QBChatMessage) {
    for attachment: QBChatAttachment in message.attachments as! [QBChatAttachment] {
        var privateUrl: String = QBCBlob.privateUrlForID(UInt(attachment.ID.toInt()!))
    }
}

Use custom parameters in a message

You can use custom parameters for the messages you send in the chat, for example to send some additional info or to send control messages:

QBChatDialog *privateChatDialog = ...;
 
QBChatMessage *message = [QBChatMessage message];
[message setCustomParameters:@{@"age": @(25)}];
//
[privateChatDialog sendMessage:message completionBlock:^(NSError *error) {
 
}];
let privateChatDialog: QBChatDialog = ...
 
let message: QBChatMessage = QBChatMessage()
message.customParameters = ["age": 25]
 
privateChatDialog.sendMessage(message, completionBlock: { (error: NSError?) -> Void in
 
});

Unread messages count

You can request total unread messages count or unread count for particular dialog:

NSSet *dialogsIDs = [NSSet setWithObject:@"55fae39ca28f9a701d0058fb"];
[QBRequest totalUnreadMessageCountForDialogsWithIDs:dialogsIDs successBlock:^(QBResponse *response, NSUInteger count, NSDictionary *dialogs) { 
    NSLog(@"Success, total count of messages:%lu", (NSUInteger)count);
    NSLog(@"Success, dialogs:%@", dialogs);
} errorBlock:^(QBResponse *response) {
 
}];
let dialogsIDs: NSSet = NSSet(array: ["55fae39ca28f9a701d0058fb"])
QBRequest.totalUnreadMessageCountForDialogsWithIDs(dialogsIDs as! Set<String>, successBlock: { (response: QBResponse, count: UInt, dialogs: [String : AnyObject]?) -> Void in
 
}) { (response: QBResponse) -> Void in
 
}


Chat history

You can choose a way to save chat message to history or not. If you decided to store - you should add save_to_history parameter to message:

QBChatMessage *message = [QBChatMessage message];
...
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"save_to_history"] = @YES;
[message setCustomParameters:params];
...
let message: QBChatMessage = QBChatMessage()
let params: NSMutableDictionary = NSMutableDictionary()
params["save_to_history"] = true
message.customParameters = params


List chat messages

If you decided to use save_to_history parameter - all chat messages will be stored to history and user can request a chat history for particular dialog.

Chat2.0 listmessages.png

To get a chat history for particular dialog use next request:

// NSMutableDictionary *extendedRequest = @{@"date_sent[gt]" : @"1231232112"}.mutableCopy;
 
QBResponsePage *resPage = [QBResponsePage responsePageWithLimit:20 skip:0];
 
[QBRequest messagesWithDialogID:@"54fda689535c125b0700bbfa" extendedRequest:nil forPage:resPage successBlock:^(QBResponse *response, NSArray *messages, QBResponsePage *responcePage) {
 
} errorBlock:^(QBResponse *response) {
    NSLog(@"error: %@", response.error);
}];
var resPage = QBResponsePage(limit:20, skip: 0)
 
QBRequest.messagesWithDialogID("54fda689535c125b0700bbfa", extendedRequest: nil, forPage: resPage, successBlock: {(response: QBResponse, messages: [QBChatMessage]?, responcePage: QBResponsePage?) in
 
}, errorBlock: {(response: QBResponse!) in
 
})

By default all messages will be marked as read after this request but you can skip this logic with a filter mark_as_read=0, just pass it into extendedRequest.

Filters

There are some filters to get only chat messages you need, not just all.


You can apply filters for the following fields:

  • _id (string)
  • message (string)
  • date_sent (timestamp)
  • sender_id (integer)
  • recipient_id (integer)
  • attachments.type (string)
  • updated_at (date)

You can apply sort for the following fields:

  • date_sent


Filters:

Operator Description Usage example
{field_name} Search records with field which contains exactly specified value extendedRequest[@"sender_id"] = @(2960);
{field_name}[{search_operator}] Search record with field which contains value according to specified value and operator.

Possible filters: lt (Less Than operator), lte (Less Than or Equal to operator), gt (Greater Than operator), gte (Greater Than or Equal to operator), ne (Not Equal to operator), in (Contained IN array operator), nin (Not contained IN array), all (ALL contained IN array), or (OR operator), ctn (Contains substring operator)
extendedRequest[@"sender_id[in]"] = @"2960,345";
limit Limit search results to N records. Useful for pagination. Default value - 100 extendedRequest[@"limit"] = @(50);
skip Skip N records in search results. Useful for pagination. Default (if not specified): 0 extendedRequest[@"skip"] = @(50);
sort_desc/sort_asc Search results will be sorted by specified field in ascending/descending order extendedRequest[@"sort_desc"] = @"date_sent"

Get a number of chat messages

There is a separate request to get a number of chat messages for particular dialog:

[QBRequest countOfMessagesForDialogID:@"554a386e6390d8c5820018a3" extendedRequest:nil
                                             successBlock:^(QBResponse *response, NSUInteger count) {
    // success
} errorBlock:^(QBResponse *response) {
    // error
}];
QBRequest.countOfMessagesForDialogID("554a386e6390d8c5820018a3", extendedRequest: nil, successBlock: {(response: QBResponse!, count: UInt) in
 
    }, errorBlock: {(response: QBResponse!) in
 
})

Delete chat messages

To delete chat messages use next snippets:

NSSet *mesagesIDs = [NSSet setWithObjects:@"560a7e8ba0eb472a6300004d", @"560a7e9ea28f9a0cda003018", nil];
 
[QBRequest deleteMessagesWithIDs:mesagesIDs forAllUsers:NO successBlock:^(QBResponse *response) {
 
} errorBlock:^(QBResponse *response) {
 
}];
QBRequest.deleteMessagesWithIDs(Set(arrayLiteral:"54fdbb69535c12c2e407c672","54fdbb69535c12c2e407c673"), forAllUsers: false, successBlock: { (response: QBResponse!) -> Void in
 
}) { (response: QBResponse!) -> Void in
 
}

NOTE: If you pass YES to the 'forAllUsers' parameter and you are the owner of the message, than message will be deleted for ALL users. If you pass NO to the 'forAllUsers' parameter, than it will be deleted only for current user and other users will still have it.

Put chat messages to history

It's also possible to create/send chat message via REST request:

QBChatMessage *msg = [[QBChatMessage alloc] init];
 
msg.text = @"Hey Mark, how are you doing?";
 
// msg.dialogID = @"54fda666535c12834e06b108";  Set the dialog Id or recipient Id
msg.recipientID = 2394285;
 
QBChatAttachment *attachment = QBChatAttachment.new;
attachment.type = @"image";
attachment.ID = @"47863";
 
[msg setAttachments:@[attachment]];
 
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"param1"] = @"value1";
params[@"param2"] = @"value2";
[msg setCustomParameters:params];
 
[QBRequest createMessage:msg successBlock:^(QBResponse *response, QBChatMessage *createdMessage) {
    NSLog(@"success: %@", createdMessage);
} errorBlock:^(QBResponse *response) {
    NSLog(@"ERROR: %@", response.error);
}];
let msg: QBChatMessage = QBChatMessage()
 
msg.text = "Hey Mark, how are you doing?"
 
// msg.dialogID = "54fda666535c12834e06b108";  Set the dialog Id or recipient Id
msg.recipientID = 2394285
 
let attachment: QBChatAttachment = QBChatAttachment()
attachment.type = "image"
attachment.ID = "47863"
msg.attachments = [attachment]
 
let params : NSMutableDictionary = NSMutableDictionary()
params["param1"] = "value1"
params["param2"] = "value2"
msg.customParameters = params
 
QBRequest.createMessage(msg, successBlock: {(response: QBResponse!, createdMessage: QBChatMessage!) in
    NSLog("success: %@", createdMessage)
}, errorBlock: {(response: QBResponse!) in
 
})

This request will put a message into history without actually sending it.

To send this message use property send_to_chat:

params[@"send_to_chat"] = @"1";
params["send_to_chat"] = "1"


Push notifications to the offline users

QuickBlox provides a way to setup automatic push notifications for the offline users. It means if your opponent is offline while you writing a message - he can automatically receive push notification.

Look at Admin panel and read how to setup automatic push notifications in Alerts section.


Dashboard

QuickBlox dashboard provide a way to view and manage all dialogs for your application. Go to admin panel, Chat module. Read more info here.


Contact list

Contact list mode

In traditional IM applications, the "buddy" system is rather straightforward. User A sends a request to become "friends" with user B. User B accepts the friend request. And now user A and B appear in each other's roster.

QuickBlox iOS SDK provides this mode.

Subscription state

To check subscription state of particular contact use subscriptionState property of QBContactListItem class. It can be the next:

  • QBPresenseSubscriptionStateNone - the user does not have a subscription to the contact's presence information, and the contact does not have a subscription to the user's presence information
  • QBPresenseSubscriptionStateTo - the user has a subscription to the contact's presence information, but the contact does not have a subscription to the user's presence information
  • QBPresenseSubscriptionStateFrom - the contact has a subscription to the user's presence information, but the user does not have a subscription to the contact's presence information
  • QBPresenseSubscriptionStateBoth - both the user and the contact have subscriptions to each other's presence information

Access to the Contact list

To access contact list you can use the contactList property in QBChat class:

[QBChat instance].contactList.contacts // array of accepted contacts
 
//
 
[QBChat instance].contactList.pendingApproval // array of pending requests
QBChat.instance().contactList!.contacts
 
QBChat.instance().contactList!.pendingApproval

You can also track contact list updates in real time by using delegates:

#pragma mark -
#pragma mark QBChatDelegate
 
- (void)chatContactListDidChange:(QBContactList *)contactList{
}
 
- (void)chatDidReceiveContactItemActivity:(NSUInteger)userID isOnline:(BOOL)isOnline status:(NSString *)status{
}
// MARK QBChatDelegate
func chatContactListDidChange(contactList: QBContactList!) {
 
}
 
func chatDidReceiveContactItemActivity(userID: UInt, isOnline: Bool, status: String!) {
 
}

Add/Remove users

To add users to the contact list just use the method below:

[[QBChat instance] addUserToContactListRequest:24123 completion:^(NSError * _Nullable error) {
 
}];
QBChat.instance().addUserToContactListRequest(24123, completion: {(error: NSError?) -> Void in
 
})

This user will receive the request to be added to the contact list:

#pragma mark -
#pragma mark QBChatDelegate
 
- (void)chatDidReceiveContactAddRequestFromUser:(NSUInteger)userID{
 
}
#pragma mark -
#pragma mark QBChatDelegate
 
func chatDidReceiveContactAddRequestFromUser(userID: UInt) {
 
}

To confirm the request:

[[QBChat instance] confirmAddContactRequest:5645 completion:^(NSError * _Nullable error) {
 
}];
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
- (void)chatDidReceiveAcceptContactRequestFromUser:(NSUInteger)userID{
 
}
QBChat.instance().confirmAddContactRequest(5645, completion: { (error: NSError?) -> Void in
 
})
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
func chatDidReceiveAcceptContactRequestFromUser(userID: UInt) {
 
}

To reject the request:

[[QBChat instance] rejectAddContactRequest:6555 completion:^(NSError * _Nullable error) {
 
}];
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
- (void)chatDidReceiveRejectContactRequestFromUser:(NSUInteger)userID{
 
}
QBChat.instance().rejectAddContactRequest(3123, completion: { (error: NSError?) -> Void in
 
});
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
func chatDidReceiveRejectContactRequestFromUser(userID: UInt) {
 
}

To remove a previously added user from the contact list:

[[QBChat instance] removeUserFromContactList:6755 completion:^(NSError * _Nullable error) {
 
}];
QBChat.instance().removeUserFromContactList(3123, completion: { (error: NSError?) -> Void in
 
});

Custom status

A client MAY provide detailed status information to his contacts by using the status parameter in presence:

[[QBChat instance] sendPresenceWithStatus:@"I shall return!"];
QBChat.instance().sendPresenceWithStatus("I shall return!")



Privacy lists

Privacy list API allows to enable or disable communication with other users in a chat. Privacy list API also enables a user to create, modify, or delete his privacy lists, define a default list.

Retrieve privacy lists names

User can have multiple privacy lists. To get a list of all your privacy lists' names use next request:

[[QBChat instance] retrievePrivacyListNames];
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
- (void)chatDidReceivePrivacyListNames:(NSArray *)listNames{
 
}
 
- (void)chatDidNotReceivePrivacyListNamesDueToError:(id)error{
 
}
QBChat.instance().retrievePrivacyListNames()
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
func chatDidReceivePrivacyListNames(listNames: [AnyObject]!) {
 
}
 
func chatDidNotReceivePrivacyListNamesDueToError(error: AnyObject!) {
 
}

Create a privacy list or edit existing list

A privacy list must have at least one element in order to create it. If no elements specified then the list with given name will be deleted. When you want to update a privacy list, it must include all of the desired items (i.e., not a "delta").

QBPrivacyItem *item = [[QBPrivacyItem alloc] initWithType:USER_ID valueForType:1022685 action:DENY];
QBPrivacyList *list = [[QBPrivacyList alloc] initWithName:@"public" items:@[item]];
 
[[QBChat instance] setPrivacyList:list];
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
- (void)chatDidSetPrivacyListWithName:(NSString *)name{
 
}
 
- (void)chatDidNotSetPrivacyListWithName:(NSString *)name error:(id)error{
 
}
var item: QBPrivacyItem = QBPrivacyItem(type: USER_ID, valueForType: 1022685, action: DENY)
var list: QBPrivacyList = QBPrivacyList(name: "public", items: [item])
 
QBChat.instance().setPrivacyList(list)
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
func chatDidSetPrivacyListWithName(name: String!) {
 
}
 
func chatDidNotSetPrivacyListWithName(name: String!, error: AnyObject!) {
 
}

QBPrivacyItem class takes 3 arguments:

  • type - use USER_ID block a user in 1-1 chat or GROUP_USER_ID to block in a group chat.
  • valueForType - ID of a user to apply an action
  • action - can be ALLOW/DENY.

Activate a privacy list

User can have multiple privacy lists, but active can be only one. In order to activate rules from a privacy list you must activate it by setting as default.

"Active" makes a list active for current chat session and "Default" makes it active for all further sessions so you don't need to activate it every time.

[[QBChat instance] setDefaultPrivacyListWithName:@"public"];
[[QBChat instance] setActivePrivacyListWithName:@"public"];
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
- (void)chatDidSetDefaultPrivacyListWithName:(NSString *)name{
 
}
 
- (void)chatDidNotSetDefaultPrivacyListWithName:(NSString *)name error:(id)error{
 
}
 
- (void)chatDidSetActivePrivacyListWithName:(NSString *)name{
 
}
 
- (void)chatDidNotSetActivePrivacyListWithName:(NSString *)name error:(id)error{
 
}
QBChat.instance().setDefaultPrivacyListWithName("public")
QBChat.instance().setActivePrivacyListWithName("public")
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
func chatDidSetDefaultPrivacyListWithName(name: String!) {
 
}
 
func chatDidNotSetDefaultPrivacyListWithName(name: String!, error: AnyObject!) {
 
}
 
func chatDidSetActivePrivacyListWithName(name: String!) {
 
}
 
func chatDidNotSetActivePrivacyListWithName(name: String!, error: AnyObject!) {
 
}

Retrieve a privacy list

[[QBChat instance] retrievePrivacyListWithName:@"public"];
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
- (void)chatDidReceivePrivacyList:(QBPrivacyList *)privacyList{
 
}
 
- (void)chatDidNotReceivePrivacyListWithName:(NSString *)name error:(id)error{
 
}
QBChat.instance().retrievePrivacyListWithName("public")
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
func chatDidReceivePrivacyList(privacyList: QBPrivacyList!) {
 
}
func chatDidNotReceivePrivacyListWithName(name: String!, error: AnyObject!) {
 
}

Delete existing privacy list

To delete a list you can call a method below or you can edit a list and set items to nil.

[[QBChat instance] removePrivacyListWithName:@"public"];
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
- (void)chatDidRemovedPrivacyListWithName:(NSString *)name{
    NSLog(@"chatDidRemovedPrivacyListWithName %@", name);
}
 
- (void)chatDidNotSetPrivacyListWithName:(NSString *)name error:(id)error{
    NSLog(@"chatDidNotSetPrivacyListWithName: %@ due to error:%@", name, error);
}
QBChat.instance().removePrivacyListWithName("public")
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
func chatDidRemovedPrivacyListWithName(name: String!) {
 
}
func chatDidNotSetPrivacyListWithName(name: String!, error: AnyObject!) {
 
}

Blocked user attempts to communicate with user

Blocked entities will be receiving an error when try to chat with a user in a 1-1 chat and will be receiving nothing in a group chat:

QBChatDialog *privateChatDialog = ...;
 
[privateChatDialog setOnBlockedMessage:^(NSError *error) {
 
    if (error != nil) {
        NSLog(@"Failed to send message with error: %@", error);
    }
}];
 
 
QBChatMessage *message = [QBChatMessage message];
[message setText:@"example message"];
 
[privateChatDialog sendMessage:message completionBlock:^(NSError *error) {
 
}];
var privateChatDialog: QBChatDialog = QBChatDialog(dialogID: "54fda689535c125b0700bbfa", type: QBChatDialogType.Private)
 
privateChatDialog.onBlockedMessage = {
    (error: NSError?) -> Void in
 
    print(error)
}
 
var message: QBChatMessage = QBChatMessage()
message.text = "example message"
 
privateChatDialog.sendMessage(message, completionBlock: { (error: NSError?) -> Void in
 
});
NSLog output:
chatDidNotSendMessage: 
 ID: 1408614930.907598 
 senderID: 1022637 
 recipientID: 1022685 
 datetime: 2014-08-21 09:55:31 +0000 
 text: example message 
 attachments: (null)
 delayed: 0 
 senderNick: (null) 
error:Error Domain=com.quickblox.chat Code=503 "Service not available." UserInfo=0xa393fd0 {NSLocalizedDescription=Service not available.}

Typing status

Many instant messaging systems include notifications about the state of one's conversation partner in a one-to-one chat (or, sometimes, in a many-to-many chat). In essence, chat state notifications can be thought of as a form of chat-specific presence.

This section describes how to integrate chat states notifications into your application.

Here is a list of common Chat State Notifications:

  1. typing: The user is composing a message. The user is actively interacting with a message input interface specific to this chat session (e.g., by typing in the input area of a chat window)
  2. paused: The user had been composing but now has stopped. The user was composing but has not interacted with the message input interface for a short period of time (e.g., 30 seconds)

With QuickBlox you can use all these chat status notifications.

Here is an example of how to implement the typing notification.

To produce this notification we should implement a UITextFieldDelegate delegate in the application:

@interface ChatViewController : UIViewController <UITextFieldDelegate>
 
@property (nonatomic, assign) IBOutlet UITextField *textField;
 
...
 
@implementation ChatViewController
 
#pragma mark
#pragma mark UITextFieldDelegate
 
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
 
    // send 'is typing' notification to opponent
    [self.someDialog sendUserIsTyping]
 
    return YES;
}
 
- (void)textFieldDidEndEditing:(UITextField *)textField{
    // finish typing
    [self.someDialog sendUserStoppedTyping]
}
class ChatViewController: UIViewController, UITextFieldDelegate {
    var dialog : QBChatDialog
    var textField: UITextField!
 
    override func viewDidLoad() {
        super.viewDidLoad()
 
        self.textField.delegate = self
    }
 
    //MARK UITextFieldDelegate
 
    func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
        self.dialog.sendUserIsTyping()
    }
 
    func textFieldDidEndEditing(textField: UITextField) {
        self.dialog.sendUserStoppedTyping()
    }
}

On the opponent's side we can track this notification and update the UI:

[self.someDialog setOnUserIsTyping:^(NSUInteger userID) {
 
}];
 
[self.someDialog setOnUserStoppedTyping:^(NSUInteger userID) {
 
}];
someDialog.onUserIsTyping = {(userID: UInt) in
 
}
 
someDialog.onUserStoppedTyping = {(userID: UInt) in
 
}


Delivered status

SDK automatically manages delivery notifications for all 'online' messages if you use marked messages.

Term 'marked' relies to messages that have automatic delivery control.

To mark a message as 'markable' use [QBChatMessage markableMessage] class method to create a message instance.

For such messages you will receive delivery confirmations.

QBChatDialog *chatDialog = ...;
 
QBChatMessage *message = [QBChatMessage markableMessage];
[message setText:@"Hi there!"];
 
[chatDialog sendMessage:message completionBlock:^(NSError * _Nullable error) {
 
}];
 
// save ID of current message
NSString *_ID = message.ID;
var chatDialog: QBChatDialog = ...
var message: QBChatMessage = QBChatMessage.markableMessage()
message.text = "Hi there!"
 
chatDialog.sendMessage(message, completionBlock: { (error: NSError?) -> Void in
 
});
 
var _ID: String = message.ID

For such messages you will receive delivery confirmation:

#pragma mark -
#pragma mark QBChatDelegate
 
- (void)chatDidDeliverMessageWithID:(NSString *)messageID dialogID:(NSString *)dialogID toUserID:(NSUInteger)userId{
 
}
#pragma mark -
#pragma mark QBChatDelegate
 
func chatDidDeliverMessageWithID(messageID: String!, dialogID: String!, toUserID userID: UInt) {
 
}

It is also possible to send a 'delivered' status manually, for example for messages received via REST API:

[[QBChat instance] markAsDelivered:message];
QBChat.instance().markAsDelivered(message)

Read status

You can manage 'read' notifications in chat. For example, User1 sends messages to User2 and User1 would like to know when User2 reads these messages.

First of all, if User1 would like to handle 'read' status of his messages, he should mark message as markable. To mark a message as 'markable' use [QBChatMessage markableMessage] class method to create a message instance.


User1 sends a message:

QBChatDialog *chatDialog = ...;
 
QBChatMessage *message = [QBChatMessage markableMessage];
[message setText:@"Hi there!"];
[message setRecipientID:1022725];
 
[chatDialog sendMessage:message completionBlock:^(NSError * _Nullable error) {
 
}];
 
// save packet ID of current message
NSString *_ID = message.ID;
var chatDialog: QBChatDialog = QBChatDialog(dialogID: "54fda689535c125b0700bbfa", type: QBChatDialogType.Private)
var message: QBChatMessage = QBChatMessage.markableMessage()
message.text = "Hi there!"
 
chatDialog.sendMessage(message, completionBlock: { (error: NSError?) -> Void in
 
});
var _ID: String = message.ID


User2 receives a message, reads it and sends 'read' status back:

- (void)chatDidReceiveMessage:(QBChatMessage *)message
{
    ...
    // read message
    ...
 
    // sends 'read' status back
    if([message markable]){
        [[QBChat instance] readMessage:message completion:^(NSError * _Nullable error) {
 
        }];
    }
}
func chatDidReceiveMessage(message: QBChatMessage!) {
    ...
    // read message
    ...
 
    // sends 'read' status back
    if message.markable() {
        QBChat.instance().readMessage(message, completion: { (error: NSError?) -> Void in
 
        })
    }
}

User1 receives a 'read' status notification that User2 has read his message:

- (void)chatDidReadMessageWithID:(NSString *)messageID dialogID:(NSString *)dialogID readerID:(NSUInteger)readerID
{
 
}
func chatDidReadMessageWithID(messageID: String!, dialogID: String!, readerID: UInt) {
 
}

Message carbons

This feature defines an approach for ensuring that all of user's devices get both sides of all conversations in order to avoid confusion. Information about the current state of a conversation is shared between all of a user's clients that enable this feature.

Is it convenient to enable carbons after chat connect:

//enable
[[QBChat instance] setCarbonsEnabled:YES];
 
//disable
[[QBChat instance] setCarbonsEnabled:NO];
//enable
QBChat.instance().setCarbonsEnabled(true)
 
//disable
QBChat.instance().setCarbonsEnabled(false)

For example, a user is online on 2 devices, iPhone and iPad. On his iPhone he sends a message to other user:

QBChatDialog *privateChatDialog = ...;
 
QBChatMessage *message = [QBChatMessage message];
[message setText:@"Hey there"];
//
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"save_to_history"] = @YES;
[message setCustomParameters:params];
//
[privateChatDialog sendMessage:message completionBlock:^(NSError * _Nullable error) {
 
}];
let privateChatDialog: QBChatDialog = ...
 
let message: QBChatMessage = QBChatMessage()
message.text = "Hey there"
 
let params : NSMutableDictionary = NSMutableDictionary()
params["save_to_history"] = true
message.customParameters = params
 
privateChatDialog.sendMessage(message, completionBlock: { (error: NSError?) -> Void in
 
});

In this moment he also receives the message on his iPad device:

- (void)chatDidReceiveMessage:(QBChatMessage *)message{
  if( message.senderID == [QBChat instance].currentUser.ID){
      // the messages comes here from carbons
  }
}
func chatDidReceiveMessage(message: QBChatMessage!) {
 if(message.senderID == QBChat.instance().currentUser.ID){
      // the messages comes here from carbons
  }
}


System notifications

There is a way to send system notifications to other users about some events. These messages work over separated channel and won't be mixed with the regular chat messages:

System notifications are not stored on a server.

It means that messages will be delivered only to online users.

QBChatMessage *message = [QBChatMessage message];
 
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"event_type"] = @"1";
params[@"value1"] = @"value1";
params[@"value2"] = @"value2";
[message setCustomParameters:params];
 
[message setRecipientID:344];
 
[[QBChat instance] sendSystemMessage:message completion:^(NSError * _Nullable error) {
 
}];
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
- (void)chatDidReceiveSystemMessage:(QBChatMessage *)message{
 
}
var message: QBChatMessage = QBChatMessage()
var params = NSMutableDictionary()
params["event_type"] = "1"
params["value1"] = "value1"
params["value2"] = "value2"
message.customParameters = params
message.recipientID = 344
 
[[QBChat instance] sendSystemMessage:message completion:^(NSError * _Nullable error) {
 
}];
 
 
#pragma mark -
#pragma mark QBChatDelegate
 
func chatDidReceiveSystemMessage(message: QBChatMessage!) {
 
}

Stream management

Stream management API allows to improve network reliability and the end-user experience. It allows to achieve 100% reliability.

More info how it works you can check in Chat API page.


Starting from iOS SDK 2.5 it is enabled by default.

In order to use it you also have to enable autoReconnect feature:

[QBChat instance].autoReconnectEnabled = YES;
QBChat.instance().autoReconnectEnabled = true

It is possible to manage a delivery timeout. By default there is no any timeout value.

[QBChat instance].streamManagementSendMessageTimeout = 10;
QBChat.instance().streamManagementSendMessageTimeout = 10

You also can enable StreamResumption. It can happen that a stream is terminated unexpectedly (e.g., because of network outages). In this case, it is desirable to quickly resume the former stream rather than complete the tedious process of stream establishment, roster retrieval, and presence broadcast.

[QBSettings setStreamResumptionEnabled:YES];
QBChat.instance().setStreamResumptionEnabled(true)

Stickers

This sample also demonstrates how to add stickers to your chat.

Installation

CocoaPods:

pod "StickerPipe", "~> 0.1.10"

Usage

You can get your own API Key on http://stickerpipe.com to have customized packs set.

Add API key in your AppDelegate.m:

[STKStickersManager initWitApiKey:@"API_KEY"];
STKStickersManager.initWitApiKey("API_KEY")

Init STKStickerController and add stickersView like inputView for your UITextView/UITextField:

 if ([STKStickersManager isStickerMessage:message]) {
    [self.stickerImageView stk_setStickerWithMessage:message placeholder:nil placeholderColor:nil progress:nil completion:nil];
}
if STKStickersManager.isStickerMessage(message) {
    self.stickerImageView.stk_setStickerWithMessage(message, placeholder: nil, placeholderColor: nil, progress: nil, completion: nil)
}

Add STKStickerPanel like inputView for your UITextView/UITextField:

@property (strong, nonatomic) STKStickerController *stickerController;
 
self.inputTextView.inputView = self.stickerController.stickersView;
[self reloadStickersInputViews];
// In your class create property
let stickerController : STKStickerController
 
self.inputTextView.inputView = self.stickerController.stickersView
self.reloadStickersInputViews()

Use delegate method for receiving sticker messages from sticker view controller:

- (void)stickerController:(STKStickerController *)stickerController didSelectStickerWithMessage:(NSString *)message {
 
    //Send sticker message
}
func stickerController(stickerController: STKStickerController!, didSelectStickerWithMessage message: String!) {
    //Send sticker message
}


Customizations

You can change default placeholders color

  • Placeholder in stickers view
[self.stickerController setColorForStickersPlaceholder:[UIColor redColor]];
self.stickerController.setColorForStickersPlaceholder(UIColor.redColor())
  • Placeholder in stickers view header
[self.stickerController setColorForStickersHeaderPlaceholderColor:[UIColor blueColor]];
self.stickerController.setColorForStickersHeaderPlaceholderColor(UIColor.blueColor())