
Many modern mobile apps rely heavily on GPS and user location data, whether it is part of an app's core functionality--such as with a navigation app--or is used more indirectly--such as for analytical and marketing purposes. There are few companies that can't benefit from contextual location user information, but it often comes at the cost of rapid battery degradation when using the default iOS or Android SDK location libraries. Enter SocialRadar's LocationKit, a direct replacement for these libraries that not only adds functionality but also reduces battery drain.

LocationKit will be a boon for companies that want to add location-based capabilities to their apps but don't have the development resources to build the features out themselves. As a direct replacement SDK, LocationKit inherits all of the features of CLLocationManager (iOS) and LocationManager (Android), but introduces a plethora of additional features.
Further, with its large database of venues, buildings and addresses, LocationKit saves on calls. After automatically detecting a significant place visit, it fetches all the contextual information:
let locationManager = LKLocationManager() locationManager.requestPlace() {(place: LKPlace?, error: NSError?) in
if let place = place {
print("The user is in: (\(place.locality))")
}
}
Here are some of the most interesting and useful features in LocationKit:
Activity Modes
LocationKit can determine the current mode of activity the user is in--whether the user is walking, driving, at home or at work--by using raw data from sensors such as the GPS and accelerometer. The standard Apple and Android SDKs don't provide a sensory way of determining the mode the user is in. If your app has been authorized access to the iOS CoreMotion manager (which utilizes the motion coprocessor), LocationKit will switch over to using that instead of the raw sensors, This will save battery life and provide higher accuracy for activity mode tracking.
On iOS, you would listen for such activity as follows:
func locationManager(manager: LKLocationManager, willChangeActivityMode mode: LKActivityMode) {
if (mode == LKActivityMode.Automotive) {
print("The user is likely driving right now")
} else {
print("The user is likely NOT driving right now")
}
}
People Nearby
The People Nearby feature makes it possible to detect other users of your app who are nearby. "Nearby" is a fluid term, according to LocationKit, but in general it refers to a city block. When an "LKPerson object" is returned, limited information about others users in range is made available. Enabling this feature in an app lets companies, for example, initiate marketing efforts or connect users.
LocationKit notes that user information is not shared with other users for security and privacy reasons. On iOS, developers would look for other nearby users, as follows:
locationManager.requestPeopleAtCurrentPlace { (people: [LKPerson]?, venue:LKVenue?, error:NSError) -> Void in
if let people = people {
print("There are \(people.count) people at \(venue.name) with you")
} else {
print("Sorry, no people found at \(venue.name) with you")
}
}
Automatic Venue Detection
LocationKit can automatically detect when a user is at a venue, using Storefront Map Database. This visit monitoring capability detects when a user enters or leaves a particular place--say, Starbucks on 2nd and Market Street. LocationKit's SDK provides two delegate methods--didStartVisit and didEndVisit--to notify the system of those locational particulars:
func locationManager(manager: LKLocationManager, didStartVisit visit: LKVisit) {
// Print out the street number and street name of the place where this
// visit started
print("Started visit at \(visit.place.subThoroughfare), \(visit.place.thoroughfare)")
}
There are many possible applications for this feature. For example, companies could use it to provide a promotion screen when a user enters a certain location--such as offering a 20% off coupon when a user enters a Starbucks between 3 p.m. an 5 p.m., or presenting a parking reservation ticket when a user enters a particular concert venue. This is accomplished regardless of whether the app is in the foreground or background, using a predictive algorithm that uses multiple sensors to pinpoint location.
Android and iOS do provide standard geofencing capabilities, but LocationKit goes above and beyond by providing contextually aware place/venue information without extra reverse geocoding calls, while maintaining efficient battery-usage of the phone.
Analytics Dashboard
LocationKit provides a robust dashboard through which a wide range of data is visualized.
For example, leveraging LocationKit’s ability to extrapolate key locations, business managers could see at a glance means when the app is being used at home and when it's used at work. you get to have a great graphical display of where your users live work, and shop. This, of course, improves companies' ability to improve “customer acquisition, app engagement and user retention."
Business managers can also have their dashboard broken down by segment--such as gender, age and income level--unlocking great customer insights without much work on the developer's part. In fact, that's the benefit of using LocationKit's analytics tool over any other popular tool, such as Google Analytics or Crashlytics: You are leveraging other LocationKit SDK features, so most of the coding effort has already been done. The only additional work developers need to do would be to pass custom event data to LocationKit's backend.
As you can see, there are some real benefits to replacing your existing default Location Manager with something that can provide greater insight while not sacrificing performance and battery life. Next up, we will show you how easy it is to integrate LocatinKit into your iOS or Android app.
Getting Started with LocationKit SDK
Regardless of which platform you will work with, the first thing you need to do is sign up, after which you should receive your API token via the Developer Dashboard. The next steps are specific to each platform, so we will start with iOS.
iOS
The easiest way to get started on iOS, is with CocoaPods, adding the following to your Podfile:
pod 'LocationKit', '~> 3.0'
Then, check in Settings > Background Modes, that Location updates is enabled.
uezVNpFAQ7yByBK91FS2
Next, in your info.plist file, add the following privacy and permission-related attributes:
NSLocationAlwaysUsageDescription
Privacy - Location Usage Description
Finally, if you don’t have one already, create an objective-c bridging header, and add the following to reference the LocationKit Objective-C library:
#import <LocationKit/LocationKit.h>
To start using LocationKit in your app thereafter, you simply need to add the import statement to the top of your file:
import LocationKit
To start lKLocationManager immediately at launch, delegate file’s didFinishLaunchingWithOptions method, and add:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let locationManager = LKLocationManager()
locationManager.debug = true //false if you are in production mode
locationManager.apiToken = "YourToken"
locationManager.startUpdatingLocation()
return true
}
Getting a Location
The simplest way to get the current location is to use the requestLocation handler method:
self.locationManager.requestLocation { (location: CLLocation?, error: NSError?) -> Void in
// We have to make sure the location is set, could be nil
if let location = location {
print("You are currently at: \(location)")
}
}
Getting a Place
To get the current place--in a format other than latitude and longitude and without having to use iOS’s reverse geocoding--you can make use of LocationKit’s requestPlace method handler, to provide more contextual location information, such as a venue or place:
locationManager.requestPlace { (place: LKPlacemark?, error: NSError?) -> Void in
if let place = place {
print("Welcome to \(place)")
} else if error != nil {
print("Uh oh, got an error: \(error)")
} else {
print("Your current place couldn't be determined")
}
}
There are of course a lot more features on iOS you can tap into so please...
Continued on page 2.
Continued from page 1.
...consult the advanced location features documentation section of LocationKit.
Android
Integration LocationKit into Android is just as easy, begining with setting up your build.gradle file:
repositories {
mavenCentral()
maven {
url 'http://maven.socialradar.com/releases'
}
}
dependencies {
compile ('socialradar:locationkit:3.0.+@aar') { transitive = true }
}
You would then connect to the LocationKit service and receive location updates, by creating a location listener, as shown below:
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {
private static final String API_KEY = "f0838784beb72a13";
private static final String LOG_TAG = "MapsActivity";
private static final String BUNDLE_KEY_LOCATION = "current_location";
private GoogleMap mMap; // Might be null if Google Play services APK is not available.
private Boolean mBound = false;
private ILocationKitBinder mLocationKit;
private Marker mMarker;
private Location mCurrentLocation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
if (savedInstanceState != null && savedInstanceState.containsKey(BUNDLE_KEY_LOCATION)) {
mCurrentLocation = savedInstanceState.getParcelable(BUNDLE_KEY_LOCATION);
}
// setUpMapIfNeeded();
}
@Override
protected void onResume() {
super.onResume();
setUpMapIfNeeded();
Intent i = new Intent(this, LocationKitService.class);
bindService(i, connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
if (outState == null) {
outState = new Bundle();
}
outState.putParcelable(BUNDLE_KEY_LOCATION, mCurrentLocation);
super.onSaveInstanceState(outState);
}
@Override
protected void onPause() {
disconnectIfNeeded();
super.onPause();
}
@Override
protected void onDestroy() {
disconnectIfNeeded();
super.onDestroy();
}
private void disconnectIfNeeded() {
if (mBound) {
mBound = false;
try {
this.unbindService(connection);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Sets up the map if it is possible to do so (i.e., the Google Play services APK is correctly
* installed) and the map has not already been instantiated.. This will ensure that we only ever
* call {@link #setUpMap()} once when {@link #mMap} is not null.
* <p/>
* If it isn't installed {@link SupportMapFragment} (and
* {@link com.google.android.gms.maps.MapView MapView}) will show a prompt for the user to
* install/update the Google Play services APK on their device.
* <p/>
* A user can return to this FragmentActivity after following the prompt and correctly
* installing/updating/enabling the Google Play services. Since the FragmentActivity may not
* have been completely destroyed during this process (it is likely that it would only be
* stopped or paused), {@link #onCreate(Bundle)} may not be called again so we should call this
* method in {@link #onResume()} to guarantee that it will be called.
*/
private void setUpMapIfNeeded() {
// Do a null check to confirm that we have not already instantiated the map.
if (mMap == null) {
// Try to obtain the map from the SupportMapFragment.
((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
.getMapAsync(this);
}
}
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
setUpMap();
}
/**
* called by the onMapReady() if the mCurrentLocation is not set, it will pick a pint near africa. It will update when it gets a location
*/
private void setUpMap() {
if (mCurrentLocation != null) {
mMarker = mMap.addMarker(new MarkerOptions().position(new LatLng(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude())).title("Marker"));
} else {
mMarker = mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
}
}
protected ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.v(LOG_TAG, "service connected");
mBound = true;
mLocationKit = (ILocationKitBinder) service;
try {
mLocationKit.startWithApiToken(API_KEY, mLocationListener);
mLocationKit.getCurrentLocation(new ILocationKitCallback<Location>() {
@Override
public void onError(Exception e, String s) {
Log.e(LOG_TAG, s, e);
}
@Override
public void onReceivedData(Location location) {
Log.v(LOG_TAG, "got location");
if (mMarker != null) {
mMarker.setPosition(new LatLng(location.getLatitude(), location.getLongitude()));
}
if (mMap != null) {
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(new LatLng(location.getLatitude(), location.getLongitude()), 19f);
mMap.moveCamera(update);
}
mCurrentLocation = location;
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mLocationKit = null;
mBound = false;
Log.v(LOG_TAG, "service disconnected");
}
};
private String getPlaceName(LKPlace place) {
if (place.getVenue() != null) {
return place.getVenue().getName();
} else if (place.getAddress() != null) {
return String.format("%s %s", place.getAddress().getStreetNumber(), place.getAddress().getStreetName());
}
return "";
}
private ILocationKitEventListener mLocationListener = new ILocationKitEventListener() {
@Override
public void onStartVisit(LKVisit lkVisit) {
Toast.makeText(MapsActivity.this, String.format("Start Visit: %s", getPlaceName(lkVisit.getPlace())), Toast.LENGTH_SHORT).show();
}
@Override
public void onEndVisit(LKVisit lkVisit) {
Toast.makeText(MapsActivity.this, String.format("End Visit: %s", getPlaceName(lkVisit.getPlace())), Toast.LENGTH_SHORT).show();
}
@Override
public void onNetworkUnavailable() {
}
@Override
public void onNetworkAvailable() {
}
@Override
public void onLocationManagerDisabled() {
}
@Override
public void onLocationManagerEnabled() {
}
@Override
public void onChangedActivityMode(LKActivityMode lkActivityMode) {
Toast.makeText(MapsActivity.this, String.format("Activity is %s", lkActivityMode), Toast.LENGTH_SHORT).show();
}
@Override
public void onError(Exception e, String s) {
}
@Override
public void onPermissionDenied(String s) {
}
@Override
public void onUnbind() {
}
@Override
public void onLocationChanged(Location location) {
Toast.makeText(MapsActivity.this, String.format("Got Updated Location"), Toast.LENGTH_SHORT).show();
if (mMarker != null) {
mMarker.setPosition(new LatLng(location.getLatitude(), location.getLongitude()));
}
if (mMap != null) {
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(new LatLng(location.getLatitude(), location.getLongitude()), 19f);
mMap.moveCamera(update);
}
mCurrentLocation = location;
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
};
}
For more information on working with the various operations and real-time information, refer to the LocationKit Android documentation.
Sample Code
For further help in getting started, there are links in the resources section, below, that will take you to sample Android and iOS sample projects.
Pricing
Pricing for LocationKit is tiered and quite decent. Location Kit is free for startups; for more robust custom reporting and individua user analytics, the price jumps up to $995 per month.
Conclusion
LocationKit is virtually a drop-in replacement for your mobile device platform’s default standard location management services. This means that with very little effort you can develop apps that provide users with a more optimized experience, better battery performance and more accurate background tracking of user locations. The SDK further enriches the default functionalities by adding always-on location, automatic venue recognition, and access to place and venue databases from within the API (among many other features). LocationKit also provides a rich graphical dashboard to allow companies to analyze where users live, work and shop, offering the opportunity for greater contextual app engagements and more targeted demographic strategies.