GeoChat - Location Based Messaging

Fully featured and highly customizable Objective-C framework to quickly add location based messaging to your app. Allow your users to browse and chat with people nearby using text, image and location messages. View Pricing
scroll down for more
Take your app to the next level by adding location based chat! Allow users to browse and chat with people nearby. Geochat can be integrated with your app in a couple of hours and provides a full solution - fast efficient user interface coupled with a real-time Firebase powered database. Checkout the demo app here.
 

You can download a demo of the standard ChatSDK here.

Geo Chat SDK

Features:

  • Browse nearby users
  • Filter by distance
  • Send text, picture and location messages
  • Create group chat rooms
  • Private messages
  • Colored speech bubbles
  • Copy/Paste
  • Automatic highlighting of emails addresses and phone numbers
  • Profile pictures for messages
  • Flexible architecture
  • Messages saved locally using CoreData
  • Tabbed interface - profile page, contacts page, search page etc...
  • Fast message deliery
  • Login via Facebook, username/ password, twitter, anonymous
  • Google + coming soon!
  • Multiple user accounts on one device
  • Cloud message storage
  • Push notifications
  • Powerful search API
  • Add custom meta data to users' profiles
  • Lazy loading of messages
  • Clean architecture 

The SDK provides a clean simple interface that completely decouples the creation of display of messages and threads from the way they're transmitted and received over the network. The SDK uses Firebase, a powerful real-time back end from Google, to deliver messages using websockets. This SDK contains a complete chat implementation including source code for the user interface and code to handle the Firebase connection.

Nearby Contacts Screen

The nearby users screen provides a list of users in your area and how far away they are.

  • Click users to instantly start a conversation with them
  • Users distances can be calculated in miles and kilometers
  • The table view updates in real-time as you move around

Thread Screen

The thread screen provides a list of the user's conversations. Single and group conversations are both supported. Each conversation has the following features:

  • Unread message indicatior
  • 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
  • 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: send a user your location, the message contains has an interactive map embedded.
  • Picture messages: select an image from an album or taken using the camera
  • Speech bubbles, re-size depending on the size of the text or image
  • Customizable speech bubble color
  • Customizable speech bubble font and font-size
  • User's profile picture is displayed next to the message
  • Bubbles scroll behind the keyboard and navigation bar
  • 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.

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 locally

The nearby contacts screen shows a list of nearby users sorted by distance.

The SDK also includes a search API allowing you to associate key words 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.

Architecture

While designing this component I put a lot of though into the best architecture to use. We 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. We 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 show stopping 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 months of development to create the capabilities of this component.

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. We 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.

iOS Support

This project supports iOS 7+

 

Pricing

14 day 14-day money-back guarantee

$399.00

GeoChat App License

  • Perpetual license

  • 1 application

  • Can distribute binary products only

  • Commercial use

  • 1 month support

$2,399.00

GeoChat Enterprise License

  • Perpetual license

  • Unlimited projects

  • Can distribute code and binary products

  • Commercial use

  • 12 months support

Documentation

Project setup

There are two ways to get started using Geochat. You can either start customizing the fully configured example project or you can add Geochat to an existing project as a dependency. In this documentation the first part will cover the process of adding Geochat to an existing project. The second part will cover the steps you need to carry out to configure Geochat with your Parse and Firebase account details.

Adding Geochat to an existing project

1. Download the project file from Binpress and unzip it to a known location

2. Add the Development folders: ChatCore, ChatCoreData, ChatFirebaseAdapter and ChatUI to the folder containing your existing project. In this example our existing project is called "Starter Kit".

Now we need to link the component parts of Geochat with your project using Cocoapods. Each component will be a development pod - that means that when Cocoapods runs, it will add each components' Xcode project to the parent project and create a simlink to the directory containing the original version of the code. More details on development pods can be found here.

If you're already using Cocoapods with your project, add the following entries to link the Geochat components as development pods:

If you're not already using Cocoapods, you will need to copy the Podfile from the sample project into your existing project and then update the target replacing 'Starter Kit' with the name of the primary target on your project. You could also update the links i.e.

pod "ChatUI", :path => "Relative or absolute path to the Chat UI folder"

3. Open the Terminal and cd to the directory of your existing project. Run:

pod install

This command will create a new .xcworkspace Xcode project file with all the project dependencies installed. Double click this file to open your project.

 

 

Note: At this point it is worth making sure you have the most up to date version of Cocoapods. Call this in your terminal to make sure:

sudo gem install cocoapods

Cocoapods isn't installed...

You must have Cocoapods set up on your computer to be able to run the pod install command. If you're not already familliar with it:

"Cocoapods is a dependency manager for Swift and Objective-C Cocoa projects. It has almost ten thousand libraries and can help you scale your projects elegantly. CocoaPods is built with Ruby and is installable with the default Ruby available on OS X."

Cocoapods helps keep all the dependencies for your projects updated meaning that you don't have to manually download and install new versions.

If you need help setting up Cocoapods then a tutorial can be found here.

Configuring Geochat

Geochat relies on several cloud services to allow it to handle geolocation, real time data and push notifications. It also uses Twitter and Facebook APIs to enable social login. Geochat comes preconfigured with using our test accounts but before you release the app you need to configure the project with your details.

Setting up the project info.plist

GeoChat stores all the keys in the info.plist file of the project.

First, to allow the app to access the user's location we need to add the location permissions, add the following two lines to the bottom of your plist:

You can customise the value to change the alert users receive when asked for permission to access their location.

Next we need to add the social media and backend keys:

Don't worry that all the entries are blank, we will fill them all in the next section.

Setting up project 

Firebase

Go to the Firebase website and create an account if you haven't already got one. Create an app on the Firebase dashboard.

Click on this new project and copy the url from your browser. It will look like: https://yourappname.firebaseio.com/. This value needs to be copied into the chatsdk_firebase_path entry in your projects info.plist file:

Firebase stores data in a tree like structure. The URL you just added to the app is the trunk of the tree and anything added after the forward slash denotes a sub branch. For example:

https://yourappname.firebaseio.com/value1
https://yourappname.firebaseio.com/value2

Each of these paths could store a piece of JSON data. Geochat uses this fact to allow you to run multiple separate chat instances on one Firebase account. So we could have:

https://yourappname.firebaseio.com/football_chat
https://yourappname.firebaseio.com/rugby_chat

In this case, people talking about football would be completely sandboxed from people talking about rugby. This extension is set using the chatsdk_firebase_root_path variable in the plist. If you're only running one instance of the chat you could choose one value for the test version and another for the production version of the app.

Before leaving the Firebase dashboard, click on the Secrets tab on the left of your Firebase dashboard. Copy your Firebase secret as you will need it for the next stage.

Parse

If you haven't got an account on Parse then go to their website and create an account. Next, create a new app on the dashboard.

We need to access the unique project keys and add them to the GeoChatSDK project. Click settings, then click keys to find the Application ID and the Client Key. We need to copy these two values into the info.plist file in the project, this will enable Parse in your project:

Note: To communicate with Parse or Firebase the user's device must be authenticated. Since the app uses both services, the user's device must be authenticated twice. The app does this as follows:

First, the app authenticates with Parse using the username and password or social login. Then it calls a Parse Cloud Code function which generates an authentication token for Firebase. Using the token, it authenticates with Firebase.

Setting up Cloud Code on Parse is very straightforward. A tutorial can be found here.

Once you have set up your cloud directory, navigate to it in finder. You should see three directories: cloud, config and public. Open the cloud folder and delete the main.js file. In the files you downloaded from Binpress, there is a folder called Cloud Functions. Copy both the files (main.js and firebase-token-generator.js) to your new cloud folder. Then open the main.js file and swap the current Firebase secret with the Firebase secret from your Firebase dashboard, the code looks like the code below:

Parse.Cloud.define("getFirebaseToken", function(request, response) {
  
  var FirebaseTokenGenerator = require("cloud/firebase-token-generator.js");
  var tokenGenerator = FirebaseTokenGenerator.initialize("UniqueFirebaseSecret"); 
  var user = Parse.User.current();
  
if(user.id != null) { var token = tokenGenerator.createToken({uid: user.id}); response.success(token); } else { response.error("User not logged in to Parse"); } });

At the end of the process, your cloud folder should now look like this:

 

Now all your files are correct, in terminal cd to your new cloud code directory.

cd [cloud code directory path]

We need to push all this code to your Parse account online. In terminal run the code:

parse deploy

You can check the code has been correctly uploaded by clicking on the Core tab in the Parse dashboard and then selecting Cloud Code in the left hand menu. It should look like this:

Social media login

If you don't want social media login for your app, you only need to hide or disable their respective buttons in the bLoginViewController. If you do want them, there is some set up required to get them working:

Facebook

Go to the Facebook developers page and create a new app for this project. Copy the app ID and once again copy this into the project's info.plist file.

1. In the .plist create a key called chatsdk_facebook_app_id and FacebookAppID, both with type string, and copy your AppID to both value fields.
2. Next create a key called FacebookDisplayName with type string and copy your app name to the value field.
3. Finally, create a key called URL types with type array. Add a dictionary as its only child. That dictionary needs one entry with the name "URL Schemes" and type array. Add a new entry to the URL Schemes array and set it's value to be " fb" followed by your AppID as seen in the image below.

Note: Make sure you add "fb" in front of your Facebook ID in the URL schemes

Note: Facebook have a quick start guide to help you get set up - see the link above.

Now go to the settings tab of your Facebook developer account and add the BundleID of your app

You must also add a contact email on this page.

You should also set single sign on to on, deep linking to off and automatic log to off:

Next, click on the Status and Review tab and click the switch to enable your app for access by the general public.

Now open your Parse dashboard, click the settings tab and click the authentication tab. Enable Facebook login and add your Facebook appID and app secret.

Finally open your project and import this library to the App delegate file:

#import "FBSDKApplicationDelegate.h"

Also add this function to the bottom of the AppDelegate.m file

// During the Facebook login flow, your app passes control to the Facebook iOS app or Facebook in a mobile browser.
// After authentication, your app will be called back with the session information.
- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation {
    return [[FBSDKApplicationDelegate sharedInstance] application:application
                                                          openURL:url
                                                sourceApplication:sourceApplication
                                                       annotation:annotation];
}

Twitter

Navigate to the Twitter developer page and create a new app for your project. Click on the Keys and Access Tokens tab to find the Twitter API key.

Add the Twitter API key into the info.plist file in your project.

Finally open your Parse dashboard and click the settings tab. Click the authentication tab and enable Twitter login and add the Twitter API keys where it says Twitter Consumer Keys.

Setting up the App Delegate

The Chat SDK needs to perform a number of actions when the app state changes. To do this, we need to add the relevant methods to the app delegate.

Make sure the app delegate imports the following libraries and that all the Chat SDK calls have been added.

#import "BAppDelegate.h"
#import "BNetworkManager.h"
#import <ChatFirebaseAdapter/BChatcatNetworkAdapter.h>
#import <ParseFacebookUtilsV4/PFFacebookUtils.h>
#import "FBSDKApplicationDelegate.h"
#import "FBSDKAppEvents.h"
#import "Parse.h"
- (void)applicationWillResignActive:(UIApplication *)application {
    
    // Since the user has quit the app we're going to set the last online indicator to zero
    [[BNetworkManager sharedManager].adapter save];
    [[BNetworkManager sharedManager].adapter goOffline];
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    [[BNetworkManager sharedManager].adapter save];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    [FBSDKAppEvents activateApp];
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    
    [[BNetworkManager sharedManager].adapter goOnline];
}

- (void)applicationWillTerminate:(UIApplication *)application {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    [[BNetworkManager sharedManager].adapter save];
}

-(void) application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    [[BNetworkManager sharedManager].adapter application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}

-(void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    [[BNetworkManager sharedManager].adapter application:application didReceiveRemoteNotification:userInfo];
}

Setting up push notifications

All the code for push notifications is already included in GeoChatSDK and getting them working only requires a small amount of configuration. For help regarding this take a look at the Parse guide for push setup here.

Note: Some of the steps in the tutorial include adding code to the app, these steps should be unnecessary as they have already been added to the project.

Architecture:

 

Front and back end

Understanding how the app conceptually fits together, will help you integrate it with your own project more quickly.

The app uses three systems to store data, Coredata, Parse and Firebase. 
 
Coredata is an ORM which comes with the iOS development environment. Each entity in the SDK is represented by an object: message, thread, user etc... When data changes on the Firebase server, the data is automatically updated and saved locally. 
 
Firebase is a real-time database which stores all the information produced by the user in a JSON format. Firebase creates a two-way data stream allowing us to save data on the remote server and register for notifications when the remote data changes.
 
Parse is a powerful online database service which is especially good for storing files and making location based queries. We use Parse for three main functions. Firstly, Parse allows us to quickly calculate the distance between users using the PFGeoPoints object. Secondly, to store photos. The URL of the photo is then stored on Firebase allowing us to access the photo quickly. Lastly, Parse is used to handle push notifications. 
 

Chat modules

The chat has been split up into 4 distinct modules. This is to enable mix and matching of parts of the app, if you want to use the entire app except the user interface then it is easy to leave out this module and still integrate the app into your project.

All the functionality from the app has been split out into four distinct libraries these are: ChatCore, ChatCoreData, ChatUI and ChatFirebaseAdapter.

CoreDataManager deals with Coredata and the data objects stored on your phone.
FirebaseNetworkAdapter deals with all network communcation.
ChatUI deals with the user interface in the app.
ChatCore contains helper classes that are required by all modules.

Login and authentication:

GeoChatSDK relies on two external web services, Parse and Firebase. For this reason, you must be authenticated with both Parse and Firebase for the app to function properly. This has been set up in two stages.

1. Login with Parse:

If you look in the BLoginViewController you can see that, although we have a function for each method of login (Facebook, Twitter, anonymous and email/password), each one leads through to the same login function:

[[BNetworkManager sharedManager].adapter authenticateWithDictionary2:@{bLoginTypeKey: @(bAccountTypeFacebook)}].thenOnMain(^id(id<PUser> user) { 

[self authenticationFinishedWithError:nil];
return Nil;
}, ^id(NSError * error) {

[self authenticationFinishedWithError:error];
return Nil;
});

AuthenticateWithDictionary2 takes a dictionary of information about the login method and then logs the user in to Parse. This method returns a promise allowing you to handle success and failure in two blocks. Here's an example of what the code does for Facebook.

[PFFacebookUtils logInInBackgroundWithReadPermissions:@[@"public_profile", @"email"] block:^(PFUser * _user, NSError *error) {



}];

Once we have been successfully authenticated, we update the user object with any information we recieved from the server. For example, if we logged in with Facebook, the server would return with the user's name and profile picture.

Note: When registering with email and password, the flow is slightly different. First we register the user. When this process finishes, we then call the authenticateWithDicationary2 method.

2. Login with Firebase

Now we need to authenticate with Firebase. To do this we use Parse to generate a custom Firebase token. To generate the token we need to call a function hosted using Parse Cloud code.

[PFCloud callFunctionInBackground:@"getFirebaseToken"

This is the code that is contained in the cloud function on Parse.

Parse.Cloud.define("getFirebaseToken", function(request, response) {
        var FirebaseTokenGenerator = require("cloud/firebase-token-generator.js");
        var tokenGenerator = FirebaseTokenGenerator.initialize("Your Firebase Secret"); 
        var user = Parse.User.current();
        
        if(user.id != null) {
                var token = tokenGenerator.createToken({uid: user.id});
                response.success(token);
        }
        else {
                response.error("User not logged in to Parse");
        }
});

We are checking if there is a user logged in with Parse. If it is, we generate a unique token for that user which will allow them to authenticate with Firebase. 

Promises

Promises are used heavily in this project and if you have never encountered them before, it’s worth taking the time to understand them conceptually before you start working on the project. Here's a guide to promises in JavaScript. Conceptually the promise implementation we use is no different. Every asynchronous function returns a promise. To handle the outcome of the task, you can add blocks to the promise using the thenOnMain function. The first block is called if the process was successful and the second block is called if the process failed.

Checkout the RXPromise library for some great examples and a very comprehensive explanation of promises if you want to learn more.

Useful methods:

We have put together some useful methods which many users will want to use when using Geochat, if you feel we’ve missed one which you'd like to see in the documentation, create a new ticket and we'll add a new section.

Add extra data to the user profile

In Geochat custom user data is stored as meta-data on the firebase database. This makes it very easy to add, remove or edit custom fields. The code is also set up with setter and getter functions to make it even easier to add your own fields:

This function sets the user's meta data

[user setMetaString:@”simon@deluge.co” forKey:bEmailKey]; 

This function gets the user’s email meta data

[user metaStringForKey:bEmailKey];

You might notice we use an email key instead of an @”email” string. Adding strings as static variables is good practice because it reduces the chances of making simple mistakes and typos.

All defines are contained in the bDefines.h file

#define bEmailKey @"email"

Lastly you need to push the user object to Firebase. This method also updates the data locally.

[[BNetworkManager sharedManager].adapter pushUser];

If I wanted to add hair color to a user's meta data, I would go through these steps:

// Add to the defines file 
#define bHairColorKey @"hairColor"

// In my code set the user data
[user setMetaString:@”Brown” forKey:bHairColorKey];

// Push the user
[[BNetworkManager sharedManager].adapter pushUser];

How to get a user object given a user ID

To get the current user is very easy, there is a specific function for this:

id<PUser> currentUser = [BNetworkManager sharedManager].adapter.currentUserModel;

To get a user with a given userID we use the following code:

id<PUser> otherUser = [[BCoreDataManager sharedManager] fetchOrCreateEntityWithID:userID withType:bUserEntity];

Create a thread with a user ID or many user IDs

It doesn't matter how many users you want in a thread, the method for creating them is the same. A thread is created with an array of user objects. There are two expected parts to thread creation,

  1. Create the thread
  2. Display the thread

Creating the thread can be achieved with the createThread function:

[[BNetworkManager sharedManager].adapter createThreadWithUsers:users threadCreated:^(NSError * error, id<PThread> thread) {
        if (!error) {
            [self pushChatViewControllerWithThread:thread];
        } 
        else {
            [UIView alertWithTitle:bErrorTitle withMessage:[NSBundle t:bThreadCreationError]];
        }
    }];

We input an array of users and the thread object is returned in the completion block.

Create or fetch a thread from its threadID

Simply call this function to fetch or create a thread with a certain threadID:

[[BCoreDataManager sharedManager] fetchEntityWithID:threadID withType:bThreadEntity];

Get a complete list of threads for a user

A user might have multiple threads and so these can be put into an array with the below function, this will fetch all the public threads a user is a member of. To fetch the private threads just use a bThreadTypePrivate type:

[[BNetworkManager sharedManager].adapter threadsWithType:bThreadTypePublic]

 

GeoLocation in GeoChatSDK

How to search for nearby users:

To search and return an array of users you need only call the function getNearbyUsers:

[[BNetworkManager sharedManager].adapter getNearbyUsers].thenOnMain(^id(NSArray * userDistances) {
        
    return Nil;
}, ^id(NSError * error) {
        
    [self searchFinishedWithUsers:@[]];
    return nil;
});

This returns an array of dictionaries, one for each nearby user. Each dictionary has two entries, the user object and the distance this user is away from the current user. This array is returned in distance order, closest to furthest.

What is a PFGeoPoint?

A PFGeoPoint is an object used by Parse which refers to a particular location. A PFGeoPoints contain a longitude and latitude value. It is very easy to find the distance between two PFGeoPoints:

PFGeoPoint * user1Point = user1[@"currentLocation"];
PFGeoPoint * user2Point = user2[@"currentLocation"];

double distanceDouble  = [user1Point distanceInKilometersTo: user2Point];

The distance value can be returned in kilometres, miles or radians. The distance function returns syncronously meaning it can be called in the main thread.

How the search works:

When using Parse, if you want to access the information of other users, you must first get permission to access this data:

PFACL * defaultACL = [PFACL ACL];
[defaultACL setPublicReadAccess:YES];
[PFACL setDefaultACL:defaultACL withAccessForCurrentUser:YES];

If you have used Parse before you will know that when requesting information, from the database, you need to use a query. Run the query and the information is returned in a completion block.

First we run a query to return the current PFUser from Parse, this makes sure our user object is up to date

PFQuery * userQuery = [PFUser query];
[userQuery whereKey:@"username" equalTo:[PFUser currentUser].username];

[userQuery findObjectsInBackgroundWithBlock:^(NSArray * objects, NSError *error) {
...
}

Now we have the up to date current user we can run another query to find users nearby

PFQuery * query = [PFUser query];
[query whereKey:bCurrentLocationKey nearGeoPoint:userGeoPoint withinMiles:100.0];
            
query.limit = 100;
            
[query findObjectsInBackgroundWithBlock:^(NSArray * objects, NSError *error) {
...
}

This query returns an array of all PFUser objects within the allowed radius.

To create threads between users we need access to the Firebase user object. We currently have the Parse user object meaning we need to do further work to convert the PFUser objects.

First we get an array of the entityIDs for each user

NSMutableArray * entityIDs = [NSMutableArray new];
for (PFUser * user in objects) {
    [entityIDs addObject: user[bEntityIDKey]];
}

We use this to download the information for each user to the Coredata database on your phone. This means we always have the most up to data details for each user e.g. profile picture, screen name.

[self usersForEntityIDs:[NSArray arrayWithArray:entityIDs]].thenOnMain(^id(id success) {
    ...                    
}, Nil); 

Now that we have each user stored on our phone we can loop through each user and fetch their PUser object. We then calculate their distance away and add both these piece of information to an NSDictionary.

Note: When Parse returns the nearby users it includes the current user. This means that we need to make sure this object is not added to the list of returned users. We do this by adding a check against the current users entityID.

NSMutableArray * users = [NSMutableArray new];
                    
for (PFUser * user in objects) {
                        
    if (![user.objectId isEqualToString:[PFUser currentUser].objectId]) {
                            
        PFGeoPoint * otherPoint = user[bCurrentLocationKey];
                            
        id<PUser> firebaseUser = [[BCoreDataManager sharedManager] fetchOrCreateEntityWithID:user[bEntityIDKey] withType:bUserEntity];
                            
        // Calculate the distance of this user from the currentUser
        double distanceDouble  = [userGeoPoint distanceInKilometersTo:otherPoint];
                            
        NSDictionary * userDict = [NSDictionary dictionaryWithObjects:@[firebaseUser, @(distanceDouble)] forKeys:@[bUserKey, bDistanceKey]];
        [users addObject:userDict];
    }
}
                    
[[BCoreDataManager sharedManager] save];
[promise resolveWithResult:[NSArray arrayWithArray:users]];

Setting the distance unit:

Parse can measure the distance between objects in miles, kilometres and radians. It is very easy to change which one is returned. This function is changed in the Parse query mentioned above:

double distanceMiles  = [userGeoPoint distanceInMilesTo:otherPoint];
double distanceKm = [userGeoPoint distanceInKilometersTo:otherPoint];
double distanceRadians = [userGeoPoint distanceInRadiansTo:otherPoint];

Updating the current user location

Updating the user’s location is dealt with by the BLocationManager. This class starts and stops the user location updating. GeoChatSDK is set up to keep the user’s location regularly updated (every 5 seconds). When a user logs on startUpdatingLocation is called and the user’s location is updated. This function is set up so it can be called multiple times but will only be running once, calling it again just resets the timer that updates the location.

Note: Careful when setting the timer to a long period of time e.g. an hour. If this method is called more regularly than the timer fires then it will appear to never be called.

If you want to start the users location updating at a certain time you can call: 

[[BNetworkManager sharedManager].adapter startUpdatingLocation];

To stop it updating call:

[[BNetworkManager sharedManager].adapter stopUpdatingLocation];

To change the frequency that the user location is updated just change the value in bLocationManager.m to set the number of seconds between updates:

#define bUserUpdateTime 5.0