Chat - Messaging SDK for iOS

Chat - Messaging SDK for iOS

Fully featured customizable Objective-C framework to display and manage message based conversations - similar to Whats App or Messages. Quickly add text, picture and location based messaging functionality to your app.

  • Language: Objective-C
    Platform(s): iPhone
  • Released: Oct 4, 2013
    Last Update: Oct 22, 2014

Be the next WhatsApp! Quickly add messaging to your app using this fully featured SDK. This SDK provides you with a full messaging system - user interface and Firebase back end.

Chat SDK - Core

The core version of the is ideal if you have your own messaging back end and need a user interface. Chat SDK core license provides you with the full front end ready to be connected to your message delivery system.

Features

  • Text, Picture and location messages
  • Group chat room
  • Private messages
  • Colored speech bubbles
  • Customize text color and font
  • Copy/Paste
  • Automatic highlighting of emails addresses and phone numbers
  • Profile pictures for messages
  • Flexible architecture
  • Messages saved with core data
  • Tabbed interface - profile page, contacts page, search page etc...

The SDK provides a clean simple interface that completely decouples the creation of display of messages and thread form the way they're transmitted and received over the network.

Chat SDK Screenshot 1

Thread Screen

The thread screen provides a list of conversations that the user is participating in. Single and group conversations are both supported. Each conversation has the following features:

  • The names of the members participating in the conversation
  • A photo of the recipient
  • Date of the last message received
  • The text of the last message received
  • Unread message indicatior
  • Threads are ordered by the date of the last message received

From this screen it's possible to create new threads and delete unwanted threads.

Message Screen

The message screen is the most important part of this project. It provides a speech bubble view of all the messages that have been received in the thread. It also allows new messages to be written and sent. Here are the main features:

  • Text messages
  • Location messages displayed on a map with customizable pin message and map scale
  • Picture messages selected from the album or taken using the camera
  • Speech bubbles re-size depending on the size of the text or image
  • Customizable speech bubble color (per-message basis)
  • Customizable speech bubble text color (per-message basis)
  • Speech bubble font and font-size set on a per-message basis
  • User's profile picture displayed next to the message
  • Bubbles scroll behind the keyboard and navigation bar
  • The phone vibrates when a new messages is received
  • The time the message was sent
  • Clicking on an image or location opens it full screen
  • Drag the view down to load older messages

These features provide a fully featured messaging platform which can quickly be interfaced with existing messaging infrastructure. If you're also looking for a back end system you should consider buying the Firebase license that provides an out-of-the box messaging platform.

Architecture

While designing this component I put a lot of though into the best architecture to use. I wanted to provide a flexible messaging front end while making it possible to interface with existing apps and network code. The final SDK completely decouples the networking functionality from the message presentation and creation. I also provide an example class that shows how to implement the necessary interfaces.

Other Open Source Projects

There are a number of free open source projects which support different aspects of messaging, none of which are very mature. This project combines the best parts of all of them while adding additional features and fixing showstopping bugs.

While creating this component, the code of about 15 different free open source projects were researched, and some are included in the package. The best bits of each were taken to make a simple, well designed, flexible component that just works. Even using the open source projects that are available, you're still looking at several weeks of development to create the capabilities of this component.

iOS Support

This project supports iOS 7+

Chat SDK - Firebase

Chat SDK Screenshot 2

The Firebase license provides a fully featured messaging platform out of the box. It uses Firebase which is a powerful real time back end which was designed from the ground up with messaging in mind. You can open an account for free - the free app would be perfectly sufficient for a small app. As your app grows you can upgrade your account - Firebase can easily scale to millions of monthly users.

Features

  • Real time chat
  • Login via Facebook, username/ password, twitter, anoymous
  • Google + coming soon!
  • Multiple user accounts on one device
  • Cloud message storage
  • Push notifications
  • Invite friends from Facebook
  • Powerful search API
  • Add custom meta data to users' profiles
  • Lazy loading of messages
  • Clean architecture

If you decide to buy the ChatSDK Firebase license you get the full ChatSDK front end as well as the Firebase networking code.

Login Screen

The login screen allows you to register or login to the messaging app. You can also use social logins via Facebook or Twitter.

Finding other users

If the user logged on using Facebook they can instantly see a list of their friends who are using the app.

Alternatively the SDK includes a search API. This allows you to associate key works with user accounts. As standard the user's name, phone number and email are all indexed. When a keyword matches an index in the search database, a list of users matching that keyword are presented to the user.

Customization

Version 3 of the Chat SDK adds a flexible way to associate data with a user. You can associate an unlimited number of meta data objects with any user - these objects can store data, text or images. This data is then synchronized across all devices. For example you could add "gender" meta data to all users.

It would then be possible to add this meta data to the search index. For example, I could pull up a list of all male users. Or all users that lived in London.

Conclusion

This network code represents a large amount of development time and will allow you to add instant messaging to your app in a couple of hours. The code is thoroughly tested with a clean object orientated architecture. If you find any bugs please report them on the Binpress bug tracker. I respond to issues with hours and will usually be able to provide a fixed version within 24 hours.

Extensions and customization

If you're looking to customize the Chat SDK, the Firebase networking code or you need a custom network integration you can get a quote using the "Need additional services?.. Get a Quote" button.

Hide

Documentation

Architecture

ChatSDK Front End

The front end is completely decoupled from the back end of the app. All communication passes through two interfaces:

  • BNetworkFacade: Exposes methods to alter server state (new message, delete thread etc...)
  • BNetworkActivityListener: Classes can implement this interface and register themselves to be notified when the server state changes

This process is controlled by the BNetworkManager singleton.

The easiest way to understand is to look at some examples:

Sending a message

In the SDK all entities are persisted using CoreData. Before creating a message, it's a good idea to create a new undo grouping. This means that if there's an error sending the message, the database state can be rolled back.

[[BCoreDataManager sharedManager].managedObjectContext.undoManager beginUndoGrouping];

Next the message is created:

BMessage * aMessage = [[BCoreDataManager sharedManager] createEntity:bMessageEntity];
[aMessage initialize];
aMessage.text = @"Test Message";
aMessage.type = @(bMessageTypeText);
aMessage.date = [NSDate date];
aMessage.user = [BNetworkManager sharedManager].currentUser;

aMessage.thread = _thread;

Here it would also be possible to customize the message's font, text color or bubble colour. Before sending we need to end the undo grouping:

[[BCoreDataManager sharedManager].managedObjectContext.undoManager endUndoGrouping];

Then we send the message using the network manager:

[[BNetworkManager sharedManager] sendMessage:aMessage withProgress:^(NSProgress * progress, bMessageStatus status) {

} completion:^(NSError * error) {
    if (error) {
        [[BCoreDataManager sharedManager].managedObjectContext.undoManager undo];
    }
}];

Creating a new thread or deleting a thread follows the same pattern. It’s just necessary to call a different method on the network manager.

Receiving a message

To receive a message it’s necessary for the class to implement the BNetworkActivityListener protocol. Then register itself with the network manager.

[[BNetworkManager sharedManager] addActivityListener:self];

When a message arrives the class will be notified:

-(void) messageAdded:(BMessage *)message toThread:(BThread *)thread {
    // Handle message here!
}

Custom back end

If you want to handle the network communication yourself you would need to create a class that conforms to the BNetworkAdapter protocol. This must then be registered with the network manager (in the app delegate):

[[BNetworkManager sharedManager] setNetworkAdapter:[[BFirebaseNetworkAdapter alloc] init]];

The network manager will be passes all the requests that are made to the network adapter. Then when the server state changes, it can notify the app by calling methods on it’s activityDelegate.

The SDK provides an example of a blank implementation. All the implementation does is writes new messages to the database but it should give you an idea of how to interact with the SDK front end.

ChatSDK Firebase

Now lets look at the architecture used by the Firebase implementation. Again all communication goes through the network manager.

There are two important classes in the Firebase back end:

  • BFirebaseNetworkAdapter - this class handles all communication between the Firebase server and the app.
  • BFirebaseInterface - is a helper class that simplifies communication

Firebase data structure

Firebase stores data in a JSON type format. Each object in the object tree can be accessed by its own unique URL. For example:

users: {
    1: {name: Ben}
    2: {name: John}
]

Here we have an array of users. We could access the users dictionary by appending “users” to our unique Firebase URL. If we wanted to access the name of user 1 we could use the URL:

[Firebase URL]/users/1

To have a look at the data structure of the app you can open the Forge data viewer in Firebase. You would need to have setup your Firebase account. Also, until you start using the app the Firebase will be blank so make sure to create a thread.

You’ll see that there are two main lists of data: threads and users. A thread contains a creation date, a list of messages and a list of users.

A user contains a FacebookID, name, image url as well as a list of threads it’s associated with.

This is a very flexible structure because it allows an arbitrary number of users per thread. It allows a user to leave the thread without disrupting the rest of the conversation.

Outbound traffic

When the app wants to modify the state of the server it calls BNetworkFacade methods on the network manager. These then get passes to the Firebase network adapter.

Lets look at an example of sending a message:

-(void) sendMessage: (BMessage *) message withProgress: (void(^)(NSProgress * progress,     bMessageStatus status)) progress completion: (void(^)(NSError * error)) completion {

    // Get the path to the thread's messages
    if (message.thread) {

        [BFirebaseInterface pushEntity:message completion:^(NSError * error) {
            completion(error);
        }];
    }
}

In the app, entities are abstracted using the PEntity interface. This allows the app to treat all entities the same for simple operations. When the adapter is told to send a message, it uses the Firebase interface to push the message to the server. In this case “push” checks if the message already exists, if it does it updates it, otherwise it creates a new message on the server.

If you look at the implementation of pushEntity, you’ll see that first it selects the entity from Firebase. If the entity exists, it updates it. Otherwise it creates a new entity on the server.

Inbound Traffic

Firebase is different to a standard database because it operates in realtime pushing data to the client. You don’t have to check if the data has updated or use push notifications! If you want to know when a piece of data has changed, you just need to listen to a location.

Firebase relies heavily on data paths. Data can be accessed using the data’s URL and we can listen to a URL to be notified when certain events happen. Firebase supports the following events:

  • Child Added
  • Child Removed
  • Child Changed
  • Child Moved
  • Value Changed

Keeping track of all the different data URLs can be quite tricky. Especially because URLs are composed of both dynamic and static parts. For example, the URL of a message contains two dynamic parts and two static:

[Firebase URL]/threads/[Thread ID]/messages/[Message ID]/…

The “Thread ID” and the “Message ID” are both dynamic - their value will change depending on the thread or message we’re looking at.

To help with this, every entity has a method called “getPath”. This method returns a path object which can be used to get the path to the entity as a string. Lets look at an example of listening to new messages

Firebase * threadRef = [[Firebase firebaseRef] appendPathComponent:thread.getPath.asString];

// We want to listen for new messages added so we listen to thread/messages
Firebase * messagesRef = [threadRef appendPathComponent:bMessagesPath];

// Get the last message from the thread
NSArray * messages = thread.messagesOrderedByDateDesc;

// If the message exists we only listen for newer messages
FQuery * query = messagesRef;
if (messages.count) {
    query = [messagesRef queryStartingAtPriority:@([((BMessage *)messages.firstObject).date timeIntervalSince1970])];
}

[query removeAllObservers];

[query observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot * snapshot) {
    // When a message arrives we add it to the database
    BMessage * message = [BFirebaseInterface objectForSnapshot:snapshot];

    // We should check that the user of the message exists to show their profile picture
    if (!message.user.lastUpdated || fabsf(message.user.lastUpdated.timeIntervalSinceNow) > bHours * 2) {
        // We need to retrieve the user
        [BFirebaseInterface selectEntity:message.user withCompletion:^(id<PEntity> user_) {
            [activityDelegate messageAdded:message toThread:thread];
        }];
    }
    else {
        [activityDelegate messageAdded:message toThread:thread];
    }
}];

First we get a pointer to the Firebase location. We do this by getting the path to the thread and append it to our base Firebase URL. We then append the string “messages” to the end of the URL to get the path to the thread’s messages.

Next we get the last message we received from the thread. We need to make a query so we only get updated if a more recent message is added. Firebase only stores simple data types - strings and numbers, so we need to convert the date to a time interval.

With the query setup, we first remove any previous observers. Then we add a new observer to the location. The observer works using a block. The block will be triggered if a new message is added. When this happens we’ll receive a new snapshot. A snapshot is basically a pseudo JSON data structure (made of NSArrays and NSDictionary) representing the data that was added.

To get the message from the snapshot we use the “objectForSnapshot” function. This function analyses the snapshot’s path to determine the data type, then recursively parses the data updating our CoreData store as it goes. I recommend that you take some time to study this function. Especially if you want to modify the Firebase storage behavior. The basic principle is that all paths are made up of alternating static and dynamic segments i.e.

threads/[Thread ID]…

If we get a snapshot with a path like this, we know that it’s of the type BThread and we can identify the particular thread using the Thread ID. Any other data stored at this level will belong to the particular thread i.e. “Creation date”.

If we have a path like this:

threads/[Thread ID]/messages/[Array of message IDs]

We know that we’re dealing with an array of messages associated with a particular thread. We would loop over the messages and for each message, the path would look like this:

threads/[Thread ID]/messages/[Message ID]

Now we know the thread ID and the message ID and the message data. This is all we need to update the database state.

Going back to the code example above, once we’ve added the message to the database, we want to make sure we also have the details of the message’s sender. First we check to see when the user was last updated, if it was more than 2 hours ago we re-load the user. When we’ve loaded the user’s data, we notify our Network Activity Listeners that a new message has been received.

Conclusion

This should give you an idea of the basic principles of the Firebase back end. The Firebase code is quite concise - the bulk of the network functionality is handled by two fairly short classes. However, conceptually it can take some time to fully understand the patterns used when working with Firebase as opposed to a standard relational database.

If you want to start really customizing this code, I’d recommend going through the two main classes:

  • BFirebaseNetworkAdapter
  • BFirebaseInterface

While looking at the excellent documentation provided by Firebase. I'm also always available to answer queries - I usually reply in less than an hour.

Hide

Setup / Installation

Chat SDK Front End

This project comes as a fully functional app which you can compile straight away in Xcode. If you're just buying a license to the front end, the app is setup using the BBlankNetworkAdapter. This adapter conforms to all the necessary protocols and can be used as a starting point to interface the app to your network.

Chat SDK Firebase

To get the Firebase version of the app working several steps are necessary:

1) Change Xcode bundle identifier

In Xcode click "Chat SDK" in the top left and identify the field called "Bundle Identifier". Change this field to [your url].Chat-SDK

2) Create a free Firebase account

Once you've created your new account you need to create a new Firebase. From the account screen click "Create new app". Once the app has been created click "View Firebase" this will open the dashboard. The dashboard shows you the data currently stored in the Firebase. Copy the URL of this page. It should be in the form [your-name].firebaseio.com. This is your base Firebase URL.

In the ChatSDK open BFirebaseDefines.h and copy your path in place of the bFirebasePath variable.

!! IMPORTANT !! The base URL path mush have a trailing slash like this:

#define bFirebasePath @"https://your-firebase-name.firebaseio.com/"

If you miss off the trailing slash the SDK won't be able to process the URLs properly and messages will not be updated.

3) Setup Facebook app

If you don't already have an account create one. Then click "Apps" -> "create new app". On the new app page click "Settings". Click "Add Platform" and choose iOS.

Fill in a display name and a contact email. Then add your bundle identifier. Set "Single Sign On" to yes. Then click "Save Changes".

Then click "Status & Review" and set the switch to "Yes" this will allow other people to sign on to your app.

Now the app is setup but you still need to link the Facebook account to Firebase and your app.

Go back to the Firebase dashboard and click "Simple Login". Click the Facebook tab and tick the "Enabled" box. Then copy paste your Facebook "App Id" and "App Secret" into the boxes provided.

Finally, open Xcode. Open BFirebaseDefines.h and copy your Facebook App Id to the variable bFacebookAppID.

Then open Chat SDK-Info.plist. Change the bFacebookAppID to your Facebook App Id. Then expand the "URL types" item. Drill down until you reach the "URL Schemes" array. Replace the part [your Facebook ID] with your Facebook ID. The URL scheme should look like this:

fbXXXXXX... // Where XXXXXX is your Facebook ID. 

4) Security

Finally, Firebase starts of with very lax security rules. That would mean that anyone could delete all the data in your database by writing one JavaScript query. To stop this, open the dashboard and click on the "Security" tab. Copy the following code:

{
  "rules": {
    ".read": true,
    ".write": "auth != null"
  }
}

This will mean that only authenticated users can modify your data. To read more about Firebase security look here.

**5) Push notifications **

Push notifications are optional so you don't have to set them up straightaway. When they're setup, the app will automatically send a push notification to the recipients of the message if they've not been online for a certain amount of time - 10 minutes by default.

To enable push notifications you need to do the following:

First you need to make a new account with Parse.com. Once you’ve registered create a new application.

Open the dashboard and select your new application. Then click “Settings” -> “Application Keys”.

Make sure that you enable "Client push enabled" in the Parse dashboard -> settings tab -> Push notifications

Then copy the Application ID and Client Key into BFirebaseDefines.h replacing bParseAppKey and bParseClientKey.

Now you need to make a push certificate with Apple. Follow the steps here:

https://www.parse.com/tutorials/ios-push-notifications

To create your push certificate and add it to your Parse account.

Make sure to change the app’s Bundle identifier to something else! When you get the app, it will be set to co.deluge.Chat-SDK. Change to:

your.domain.Something

Push notifications will not work on the simulator. To test this you’ll need to use a real device.

Make sure you create a new development provisioning profile. Usually you can develop using you phone using a default Xcode-generated development profile. This won't work with push notifications. Log in to the Apple member's center and create a new Development provisioning profile once you've created, downloaded and installed the certificate, go to Build Settings in Xcode and set Provisioning Profile to your new development profile.

Now the app should be ready to go!

If you have any problems Firebase offer good documentation and Facebook have an integration guide.

Hide

FAQ

In this section I'm going to put answers to frequently asked questions.

Storyboard integration

Many people think that Storyboards and XIBs can't be used together. That you either have to create the whole app in one monolithic Storyboard or you have to use hundreds of interconnected XIB files. In fact, Storyboards are just an extra tool that can be used for interface creation along side XIBs and code. In some situations it's useful to use a Storyboard and in others it's better to use an XIB.

Lets say that you want to launch the Threads view from your Storyboard using a button. First, you'd need to right click drag the button from the Storyboard into the relevant .m file to create an action. You'd end up with an action like this:

- (IBAction) openChatButtonPressed:(UIButton *)sender {

}

Now, whenever you click this button, this code will be called.

Inside this method we're going to load up the threads view controller of the group Chat SDK.

[self.navigationController pushViewController:[[BThreadsViewController alloc] initWithNibName:Nil bundle:Nil] animated:YES];

This will create a new instance of the threads view (obviously in the Firebase version, you would have had to have authenticated with Firebase before doing this). Since Storyboards use the same navigation controller as XIBs, we can just push the new view onto the stack.

To get back to our Storyboard, we would just pop the view controller:

[self.navigationController popViewControllerAnimated:YES];

This will take us back to the previous Storyboard view.

How is a user created?

To find where a user is created we need to look at the BFirebaseNetworkAdapter class. When the app first loads up it will try to authenticate the user with Facebook. Once that's successful it will call:

syncWithProgress: withCompletion:

The purpose of this method is to get the app up-to-date with the server. Lets go through the function in detail to see what's happening.

BUser * user = [[BCoreDataManager sharedManager] fetchOrCreateUserWithEntityID:Nil withFacebookID:facebookID];
user.facebookID = facebookID;

First we fetch or create a new user object with the appropriate facebook ID. If a user already exists, we'll be returned it's instance, otherwise a new user object will be added to the database.

Next we call:

[BFirebaseInterface selectEntity: user withCompletion...

In Firebase there are two ways we can access a piece of data directly, using the Firebase ID or by using the priority of the object. A priority is a user determined identifier for the data.

The selectEntity method first checks to see if an "Entity ID" exists on the entity. In this case the entity id points to the Firebase ID of the object (see user.entityID method). If the entity id doesn't exist, it will check to see if the entity has a priority. In this case, we're using the user's Facebook ID as the priority but really you could use any external ID (see user.getPriority).

Then the code calls the Firebase method:

observeSingleEventOfType:FEventTypeValue withBlock:...

This will check Firebase and return a snapshot of the data. When the snapshot arrives, it's processed using object for snapshot: (explained in more detail earlier in this documentation)

[BFirebaseInterface objectForSnapshot:snapshot];

After this, it will return the resulting BUser object to the completion block which takes us back to the sync method.

If this is the first time the user has logged on, they won't yet have a Firebase ID. In this case, user.entityID will be Nil. Since, a new user will have been generated when we called the select method, we need to delete the original "dummy" user we created and continue using the user_ variable provided by the block.

Next we retrieve the user's profile picture from Facebook. When the return block of that method is called, we add the profile picture to the user. Finally, we want to make sure that the user's details are all up-to-date on Firebase.

To do this we call:

BFirebaseInterface pushEntity: user_ completion: ...

The pushEntity method checks to see if an entity exists, if it does, it updates that entity, otherwise it creates a new entity.

Lets look at that method in more detail.

First, we call selectEntity which retrieves existing data from Firebase (as described earlier).

If the entity exists already on Firebase, it will have a Firebase ID (entity ID). In this case, we call:

ref updateChildValues: entity.asDictionary ...

Otherwise we need to create a new entity on Firebase using:

Firebase * listRef = [ref childByAutoId];

// We can set the name here!
[entity setEntityID:listRef.name];

[listRef setValue:entity.asDictionary andPriority:priority withCompletionBlock:^(NSError * error, Firebase * firebase) {
    [entity setEntityID:firebase.name];
    completion(error);
}];

First we create a new Firebase identifier - this creates space for a new item in Firebase. Then we update the entity object's ID.

It's necessary to do this here because otherwise a race condition can occur (the user object in the database doesn't have an entity ID and new data arrives from Firebase before the "withCompletionBlock" block is called. The new information looks for the user object in the local database using the entity id, because it doesn't find it, it creates a new user object).

Then we call setValue... This creates a new user object in Firebase.

Going back to the sync method. The final step is to add observers to the user object. These observers are callback methods that are called whenever a relevant piece of information about the user, their threads or messages changes on the server. Whenever a piece of data changes, the local database is updated by the method:

[BFirebaseInterface objectForSnapshot: ...];

The important thing to note about the user creation process is that a user is identified by both it's entity id (Firebase ID) and it's priority (Facebook ID). However, the priority could be any id, the user's row id on your server, the user's Twitter ID etc...

Hide

Adding a new database field

How to add a new database field:

If you want to extend the chat component to add your own data you're going to need to add new fields both to the app's CoreData storage and to Firebase.

CoreData

First we need to add the property to the CoreData schema. Open ChatSDK.xcdatamodeld in the left navigation bar and create a new version of the model.

Editor->Add Model Version

Call it DataModel v[next version number]

Click ChatSDK.xcdatamodeld again and open the right side bar. Click the icon that looks like a piece of paper at the top then change Model Version to the new model version you created.

In the left navigation bar again, expand the ChatSDK.xcdatamodeld by clicking the little arrow and select the model version. After that make the changes you need to make to the entities.

In this example, I’m going to add a last online property to the user entity.

Under the “entities” heading click the BUser entity. Under attributes click + to add a new attribute. Set the name for the variable and choose it’s type. In this case we choose Date.

Now we need to update the entities. Right click on the left in the CoreDataEntities folder and choose New File then choose Core Data then NSManagedObject subclass, select the new DataModel you created then select all the entities. Click Next and select the folder CoreDataEntities. Click Create.

This will write over the existing entities updating them with the properties you added.

CoreData will automatically update the database when you next open the app.

Next we need to update the User protocol. The user protocol encapsulates the user object. This means that if you wanted to design your own user object or use a different storage type, you could implement the protocol with your new object and it would work perfectly with the existing chat component.

Open PUser.h and add a new method to access the last updated property.

-(NSDate *) lastOnline; -(void) setLastOnline: (NSDate *) lastOnline;;

Now we can access the variable from within the chat component and we can save the variable to CoreData.

Firebase

Now we need to make sure that the variable can be saved in Firebase.

The Chat SDK comes with a framework which simplifies working with Firebase.

In order for our objects to be added to Firebase they need to be serialized. When we receive an object from Firebase it then needs to be de-serialized. The framework will handle most of the details but we still need to add the serialization and deserialization code.

Open BUser+Additions.h and find the method called “asDictionary”. We need to add this variable to the dictionary so when the user is uploaded this variable is set as well.

First we’re going to create a new key. Open BDefines.h and add a new key:

#define b_LastOnline @"last-online”

Then add the property to the user’s dictionary:

return @{b_Name: self.name,
         b_FacebookID: self.facebookID,
         b_PictureURL: self.pictureURL,
         b_PictureExists: self.pictureExists,
         b_LastOnline: @(self.lastOnline.timeIntervalSince1970)};

Now when we push a user the lastOnline value will also be pushed. We add the time interval since 1970 because Firebase can only store basic data types - not NSDates.

Now we need to make sure that when we are pushed a new user from the Firebase, that the locally stored user object is also updated.

Look at the updateFromDicationary method in BUser+Additions.m. This method updates the object when provided with a dictionary of serialized data.

NSNumber * lastOnline = snapshot.value[b_LastOnline];
if (lastOnline) {
    user.lastOnline = [NSDate  dateWithTimeIntervalSince1970:lastOnline.doubleValue];
}

Here we extract the last online time from the user and add it to the database object.

That's pretty much it. Now all we need to do is set the value and when we select or push a user object, the last online field will be updated automatically.

You need to log-in or create an account
  • Create an account
  • Log-in
Please use your real name.
Activation link will be sent to this address.
Minimum 8 characters
Enter your password again

Clicking this button confirms you read and agreed to the terms of use and privacy policy.

X

Save your watchlist

Fill your details below to receive project updates from your watch list - including new versions, price changes and discounts.

I agree to the terms of use and privacy policy.

5 licenses, starting from From » $99.99 14 day money-back guarantee View Licenses
or Get a quote

for customization or integration services

  • I am an app developer with 15 live apps on the App Store and one that made it to #8 Overall in the U.S. App Store and made it to #1 in various countries around the world. I'm serious about making apps and have spent thousands of dollars buying source code. I know my way around source code and the more I buy, regardless of how cheap or expensive the code, the more I realize it's a coin toss that the source code will actually be as good as you hoped. There's a lot of bad source code being sold out there on various sites that's full of bugs with sellers who have no idea how to fix the code and you end up either wasting money or wasting time.

    I never expect code to be 100% perfect because people are human and since the sellers / developers don't have every possible device made, in order to test the code, it's easy to miss things here or there. However, what I do expect is that the person selling the code stands behind their code and when problems are pointed out, they don't get defensive and make excuses. Too many times the bad developers blame their bad code on the buyer for not following directions. Those excuses never work on me because I can go through the code and tell them exactly where the problem is and why it's not working.

    Buying the Chat SDK project from BinPress & DComponents is one of the best experiences I've had with buying code! Ever!! The first thing that stood out was the fact that Ben, the developer, was answering all of the questions on the comments section professionally and clearly. A lot of time you get these angry, testy developers that think their code is flawless and how dare you question anything. With Ben it seemed like he had a cool confidence in what he was selling but also understood there can always be room for improvements and was willing to listen.

    I contacted him to get the beta version of the Chat SDK app that he had on Test Flight. Once I got everything up and running on my iPhone 5s running iOS 7.0.4, I noticed that the app crashed doing certain tasks. I contacted Ben and he offered to go over the issues with me on Skype. There were no issues with iOS 7.1.1 but I hadn't upgraded yet. After a few minutes, he quickly figured out the problem, fixed it, and had the new version uploaded while we were still chatting. Now that's somebody who knows his stuff! Ben get's the highest marks possible for knowledge and professionalism. Oh and one thing I forgot to mention... we did all of this BEFORE I even bought the code. Somebody who takes that much pride in his code is gold in my book. I was completely sold.

    As for the code itself, it's as smooth as butter over hot fluffy pancakes. It opens up in Xcode 5.1 with zero Issues and compiles to iOS 7.1 with no errors or warnings. Ahhh... that's nice. Chat Apps are already big but Whatsapp selling for billions of dollars is making this sector even hotter and I think this is the best chat source code out there and I've looked everywhere. I'm looking forward to seeing all the awesome things I can do with this code. The good part is knowing you have a great developer like Ben standing behind it. I hope this was helpful to those who are thinking about buying. I think it's definitely worth it.

    As soon as I make my 1st project with the code, I'll make sure to post it here. In the meantime, if you want to ask me anything specific that I didn't cover here, hit me on Twitter @GregStorm.
    G Gregory
    6 months ago, 0 comments
    Was this helpful?
    Flag 8 of 8 people found this review helpful
  • The code is clean, powerfull and well shaped.
    It is a real pleasure to read and modify it.
    But it requires some good development skills, and it is the best tutorial for Xcode and IOS development I have found.
    ZP Zeno Pokossy
    5 months ago, 0 comments
    Was this helpful?
    Flag 2 of 2 people found this review helpful
  • Chat SDK is very powerful. I can easily integrate it with my project. The latest updates save me lots of time to build the real product.

    I highly recommended it:
    1. Notes and documents are clear.
    2. Message database is well organised.
    3. Interface UI is flexible to be extended.

    Thank you Ben. You did a good job!
    CC Cookie Chiang
    6 months ago, 1 comments
    Was this helpful?
    Flag 2 of 2 people found this review helpful
Post a comment

Or enter your name and Email
  • DP Daniel Prundianu 18 hours ago
    I have one more question about the user name which are used for registration. I would like to register with the phone number. The number will be validate by Nexmo with a code. The registration is clear, if we say find friends from telephone book maybe the user did not saved the number in correct format... Example: my validate number is in this format: +43 664 4031111 in my contact list I have bob with number 06644031122 but the server know that Bob has number like this +436644031122... What would be best practise to solve this problem... Is it already implemented in your sdk? I thought about sql statements where I can search with LIKE? But is this "unique"? +43 is the international format for Austria! Best regards Daniel
    • Ben Smiley Developer 17 hours ago
      Hi Daniel, It would be possible to register users using a phone number. But it would require you to have a separate server. On registration, the user would validate their phone number, then make a request to your server which would generate a Firebase authentication token. In terms of phone number validation there are two issues - making sure the phone number the user uses to log on is in a particular format and searching using contacts. The first you can solve by forcing the user to register with a phone number with a particular format i.e. country code + number and then performing validation. For the second part, I think the easiest thing to do would be to validate the phone numbers before making the search request i.e. you get a phone number from the contacts and then try to make it match the correct format - if there's a country code you use the number directly, otherwise you'd need to ask the user to set a default country code for their contacts. Thanks, Ben
  • MS majid sport 4 days ago
    Is it possible to change the language? Arabic. right to left
    • Ben Smiley Developer 3 days ago
      Most (95%) of the text in the app is localized so you could translate the app to Arabic by including the necessary localization files. I've checked, and iOS automatically changes text inputs to be right aligned - you could then go through right align any other text as necessary.
  • A AJ 5 days ago
    Regarding future support of the app; when IOS firmware upgrades are made, will you be updating the SDK every time to keep it compatible top newer firmware releases? And if so, would all those who bought previous versions get access to newest sdk with firmware upgrades?
    • Ben Smiley Developer 5 days ago
      Hi, the app is always updated for the latest iOS version. Also, the new updates are made available to all existing license holders. Thanks, Ben
    • A AJ 5 days ago
      Thank you kindly for the prompt response Ben. One last inquiry, will an Android version be surfacing any time soon?
    • Ben Smiley Developer 3 days ago
      The Android version is in beta - and I'm making updates to the iOS version so the two are compatible.