Building a simple app beaconing solution: Part 2

In part 1 of this series I talked about building the Android app to beacon to a Splunk server its location. In part 2, I’ll cover the iOS app that does the same thing.

As in part 1, I will not be covering basic iOS app development. There are a ton of resources and online courses (Udemy, Lynda among others) that cover this. I assume you already know how to build a simple single “page” (or ViewController) app in Objective-C (not Swift) with a Button and TextField, what a IBOutlet and IBAction is, how to dismiss the keyboard on tap outside TextField etc.

The code can be found here (under the iOS folder): https://github.com/foohm71/SplunkBrother

The first part is how we persist the deviceID on the device (just like what we did for the Android app). The code is in the viewDidLoad() method in ViewController.m. The key is using the NSFileManager object. This is pretty standard code that you can find online. One source is here: http://www.ios-developer.net/iphone-ipad-programmer/development/file-saving-and-loading/using-the-document-directory-to-store-files . Essentially the code to get the data stored in “deviceID.dat” is here:

    // this part gets the saved deviceID and displays it
    NSFileManager *fileMgr;
    NSString *documentDir;
    NSArray *directoryPaths;
    
    fileMgr = [NSFileManager defaultManager];
    directoryPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    documentDir = [directoryPaths objectAtIndex:0];
    
    filepath = [[NSString alloc] initWithString:[documentDir stringByAppendingPathComponent:@"deviceID.dat"]];
    
    if ([fileMgr fileExistsAtPath:filepath]) {
        
        deviceID = [NSKeyedUnarchiver unarchiveObjectWithFile:filepath];
        deviceIdInput.text = deviceID;
    }

The corresponding code to use the value in the TextField to store in the “deviceID.dat” can be found at the submitButton() IBAction method:

- (IBAction)submitButton {
    NSString *deviceID;
    
    deviceID = deviceIdInput.text;
    
    [NSKeyedArchiver archiveRootObject:deviceID toFile:filepath];
    
}

Note: deviceIdInput is the TextField. We already have initialized “filepath” in ViewDidLoad() so this would work.

The next part we’ll talk about is the splunkPing also in ViewController.m. This is essentially the code to beacon the location (once obtained) to Splunk. This basically uses a NSMutableURLRequest object to make the HTTP Post to the Splunk endpoint (in this case localhost for testing). The rest of the code is pretty standard code to make a HTTP request. An example can be found here: http://codewithchris.com/tutorial-how-to-use-ios-nsurlconnection-by-example/ 

The last 2 parts are really the meat of this project: (a) creating a “background service” and (b) obtain lat/long info to beacon. These 2 go hand in hand because (surprise, surprise) iOS only allows for 5 background modes – see https://www.raywenderlich.com/29948/backgrounding-for-ios . The good news is receiving location updates is one of them ie. we are able to write callbacks to trigger when location changes.

The setup code for this can be found in the ViewDidLoad() method:

    
    locationMgr = [[CLLocationManager alloc] init];
    locationMgr.delegate = self;
    // locationMgr.distanceFilter = kCLDistanceFilterNone;
    locationMgr.desiredAccuracy = kCLLocationAccuracyBest;
    [locationMgr startUpdatingLocation];
    NSLog(@"LocationManager started");

Next we need to write the handlers for 2 cases: (a) when there’s an error and (b) when location is updated. Note that unlike the Android app where we needed a try-catch block to handle no location received, here Apple already provides the hook in the form of the error method.

-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{

    [self splunkPing:deviceID withLatitude:0.0 withLongitude:0.0];
    NSLog(@"Error: %@",error.description);
}

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
    // If it's a relatively recent event, turn off updates to save power.
    CLLocation* location = [locations lastObject];
    NSDate* eventDate = location.timestamp;
    NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
    NSString *coord;
    
    NSLog(@"Inside didUpdateLocations");
    
    if (fabs(howRecent) < sleepInSeconds) {
        // If the event is recent, do something with it.
        NSLog(@"latitude %+.6f, longitude %+.6f\n",
              location.coordinate.latitude,
              location.coordinate.longitude);
        coord = [[NSString alloc] initWithFormat:@"%f,%f",location.coordinate.latitude, location.coordinate.longitude];
        
        [self splunkPing:deviceID withLatitude:location.coordinate.latitude withLongitude:location.coordinate.longitude];
        
    }
}

This is pretty standard and you can find example code from the link listed above as well. Please note, for this to work you need to enable permissions in the project’s Info.plist – see the section “Receiving Location Updates” in the link.

Advertisements

Leave a comment

Filed under Uncategorized

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s