Skip to content

Instantly share code, notes, and snippets.

@ger86
Created January 16, 2019 15:39
Show Gist options
  • Save ger86/08c0eaeb8f6dee4fd3dabdefc0d7baba to your computer and use it in GitHub Desktop.
Save ger86/08c0eaeb8f6dee4fd3dabdefc0d7baba to your computer and use it in GitHub Desktop.
//
// RNiBeacon.m
// RNiBeacon
//
// Created by MacKentoch on 17/02/2017.
// Copyright © 2017 Erwan DATIN. All rights reserved.
//
#import <CoreLocation/CoreLocation.h>
#import <React/RCTBridge.h>
#import <React/RCTConvert.h>
#import <React/RCTEventDispatcher.h>
#import "RNiBeacon.h"
@interface RNiBeacon() <CLLocationManagerDelegate>
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (assign, nonatomic) BOOL dropEmptyRanges;
@end
@implementation RNiBeacon
RCT_EXPORT_MODULE()
#pragma mark Initialization
- (instancetype)init
{
if (self = [super init]) {
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
// Options to allow app killed state running
self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
self.locationManager.allowsBackgroundLocationUpdates = true;
self.locationManager.pausesLocationUpdatesAutomatically = NO;
self.dropEmptyRanges = NO;
}
return self;
}
- (NSArray<NSString *> *)supportedEvents
{
return @[
@"authorizationStatusDidChange",
@"beaconsDidRange",
@"regionDidEnter",
@"regionDidExit",
@"didDetermineState"
];
}
#pragma mark
-(CLBeaconRegion *) createBeaconRegion: (NSString *) identifier
uuid: (NSString *) uuid
major: (NSInteger) major
minor:(NSInteger) minor
{
NSUUID *beaconUUID = [[NSUUID alloc] initWithUUIDString:uuid];
unsigned short mj = (unsigned short) major;
unsigned short mi = (unsigned short) minor;
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:beaconUUID major:mj
minor:mi
identifier:identifier];
beaconRegion.notifyOnEntry = YES;
beaconRegion.notifyOnExit = YES;
beaconRegion.notifyEntryStateOnDisplay = YES;
return beaconRegion;
}
-(CLBeaconRegion *) createBeaconRegion: (NSString *) identifier
uuid: (NSString *) uuid
major: (NSInteger) major
{
NSUUID *beaconUUID = [[NSUUID alloc] initWithUUIDString:uuid];
unsigned short mj = (unsigned short) major;
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:beaconUUID
major:mj
identifier:identifier];
beaconRegion.notifyOnEntry = YES;
beaconRegion.notifyOnExit = YES;
beaconRegion.notifyEntryStateOnDisplay = YES;
return beaconRegion;
}
-(CLBeaconRegion *) createBeaconRegion: (NSString *) identifier
uuid: (NSString *) uuid
{
NSUUID *beaconUUID = [[NSUUID alloc] initWithUUIDString:uuid];
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:beaconUUID
identifier:identifier];
beaconRegion.notifyOnEntry = YES;
beaconRegion.notifyOnExit = YES;
beaconRegion.notifyEntryStateOnDisplay = YES;
return beaconRegion;
}
-(CLBeaconRegion *) convertDictToBeaconRegion: (NSDictionary *) dict
{
if (dict[@"minor"] == nil) {
if (dict[@"major"] == nil) {
return [self createBeaconRegion:[RCTConvert NSString:dict[@"identifier"]]
uuid:[RCTConvert NSString:dict[@"uuid"]]];
} else {
return [self createBeaconRegion:[RCTConvert NSString:dict[@"identifier"]]
uuid:[RCTConvert NSString:dict[@"uuid"]]
major:[RCTConvert NSInteger:dict[@"major"]]];
}
} else {
return [self createBeaconRegion:[RCTConvert NSString:dict[@"identifier"]]
uuid:[RCTConvert NSString:dict[@"uuid"]]
major:[RCTConvert NSInteger:dict[@"major"]]
minor:[RCTConvert NSInteger:dict[@"minor"]]];
}
}
-(NSDictionary *) convertBeaconRegionToDict: (CLBeaconRegion *) region
{
if (region.minor == nil) {
if (region.major == nil) {
return @{
@"identifier": region.identifier,
@"uuid": [region.proximityUUID UUIDString],
};
} else {
return @{
@"identifier": region.identifier,
@"uuid": [region.proximityUUID UUIDString],
@"major": region.major
};
}
} else {
return @{
@"identifier": region.identifier,
@"uuid": [region.proximityUUID UUIDString],
@"major": region.major,
@"minor": region.minor
};
}
}
-(NSString *)stringForProximity:(CLProximity)proximity {
switch (proximity) {
case CLProximityUnknown: return @"unknown";
case CLProximityFar: return @"far";
case CLProximityNear: return @"near";
case CLProximityImmediate: return @"immediate";
default: return @"";
}
}
RCT_EXPORT_METHOD(requestAlwaysAuthorization)
{
if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
}
}
RCT_EXPORT_METHOD(requestWhenInUseAuthorization)
{
if ([self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
[self.locationManager requestWhenInUseAuthorization];
}
}
RCT_EXPORT_METHOD(allowsBackgroundLocationUpdates:(BOOL)allow)
{
self.locationManager.allowsBackgroundLocationUpdates = allow;
}
RCT_EXPORT_METHOD(getAuthorizationStatus:(RCTResponseSenderBlock)callback)
{
callback(@[[self nameForAuthorizationStatus:[CLLocationManager authorizationStatus]]]);
}
RCT_EXPORT_METHOD(getMonitoredRegions:(RCTResponseSenderBlock)callback)
{
NSMutableArray *regionArray = [[NSMutableArray alloc] init];
for (CLBeaconRegion *region in self.locationManager.monitoredRegions) {
[regionArray addObject: [self convertBeaconRegionToDict: region]];
}
callback(@[regionArray]);
}
RCT_EXPORT_METHOD(startMonitoringForRegion:(NSDictionary *) dict
startMonitoringResolver:(RCTPromiseResolveBlock)resolve
startMonitoringRejecter:(RCTPromiseRejectBlock)reject)
{
// App killed State Running
[self.locationManager startMonitoringSignificantLocationChanges];
[self.locationManager startMonitoringForRegion:[self convertDictToBeaconRegion:dict]];
NSDictionary *lastEvent = [[NSUserDefaults standardUserDefaults] objectForKey:[dict objectForKey:@"uuid"]];
resolve(lastEvent);
}
RCT_EXPORT_METHOD(startRangingBeaconsInRegion:(NSDictionary *) dict)
{
[self.locationManager startRangingBeaconsInRegion:[self convertDictToBeaconRegion:dict]];
}
RCT_EXPORT_METHOD(stopMonitoringForRegion:(NSDictionary *) dict)
{
// App killed State Running
[self.locationManager stopMonitoringSignificantLocationChanges];
[self.locationManager stopMonitoringForRegion:[self convertDictToBeaconRegion:dict]];
}
RCT_EXPORT_METHOD(stopRangingBeaconsInRegion:(NSDictionary *) dict)
{
[self.locationManager stopRangingBeaconsInRegion:[self convertDictToBeaconRegion:dict]];
}
RCT_EXPORT_METHOD(startUpdatingLocation)
{
[self.locationManager startUpdatingLocation];
}
RCT_EXPORT_METHOD(stopUpdatingLocation)
{
[self.locationManager stopUpdatingLocation];
}
RCT_EXPORT_METHOD(shouldDropEmptyRanges:(BOOL)drop)
{
self.dropEmptyRanges = drop;
}
-(NSString *)nameForAuthorizationStatus:(CLAuthorizationStatus)authorizationStatus
{
switch (authorizationStatus) {
case kCLAuthorizationStatusAuthorizedAlways:
return @"authorizedAlways";
case kCLAuthorizationStatusAuthorizedWhenInUse:
return @"authorizedWhenInUse";
case kCLAuthorizationStatusDenied:
return @"denied";
case kCLAuthorizationStatusNotDetermined:
return @"notDetermined";
case kCLAuthorizationStatusRestricted:
return @"restricted";
}
}
// Allow location update in app killed state
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
//Do nothing here, but enjoy ranging callbacks in background :-)
}
// Allow location update in app killed state
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
//Do nothing here, but enjoy ranging callbacks in background :-)
}
-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
NSString *statusName = [self nameForAuthorizationStatus:status];
[self sendEventWithName:@"authorizationStatusDidChange" body:statusName];
}
-(void)locationManager:(CLLocationManager *)manager rangingBeaconsDidFailForRegion:(CLBeaconRegion *)region withError:(NSError *)error
{
}
-(void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error {
}
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
}
-(NSString *)stringForState:(CLRegionState)state {
switch (state) {
case CLRegionStateInside: return @"inside";
case CLRegionStateOutside: return @"outside";
case CLRegionStateUnknown: return @"unknown";
default: return @"unknown";
}
}
- (void) locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
NSDictionary *event = @{
@"state": [self stringForState:state],
@"identifier": region.identifier,
};
[self sendEventWithName:@"didDetermineState" body:event];
}
-(void) locationManager:(CLLocationManager *)manager didRangeBeacons:
(NSArray *)beacons inRegion:(CLBeaconRegion *)region
{
if (self.dropEmptyRanges && beacons.count == 0) {
return;
}
NSMutableArray *beaconArray = [[NSMutableArray alloc] init];
for (CLBeacon *beacon in beacons) {
[beaconArray addObject:@{
@"uuid": [beacon.proximityUUID UUIDString],
@"major": beacon.major,
@"minor": beacon.minor,
@"rssi": [NSNumber numberWithLong:beacon.rssi],
@"proximity": [self stringForProximity: beacon.proximity],
@"accuracy": [NSNumber numberWithDouble: beacon.accuracy],
@"distance": [NSNumber numberWithDouble: beacon.accuracy],
}];
}
NSDictionary *event = @{
@"region": @{
@"identifier": region.identifier,
@"uuid": [region.proximityUUID UUIDString],
},
@"beacons": beaconArray
};
[self sendEventWithName:@"beaconsDidRange" body:event];
}
-(void)locationManager:(CLLocationManager *)manager
didEnterRegion:(CLBeaconRegion *)region {
NSDate *now = [NSDate date];
NSDictionary *regionDict = [self convertBeaconRegionToDict: region];
NSDictionary *event = @{
@"region": regionDict,
@"type": @"didEnter",
@"date": [NSNumber numberWithUnsignedInteger:now.timeIntervalSince1970]
};
[[NSUserDefaults standardUserDefaults] setObject:event forKey:regionDict[@"uuid"]];
[[NSUserDefaults standardUserDefaults] synchronize];
[self sendEventWithName:@"regionDidEnter" body:event];
}
-(void)locationManager:(CLLocationManager *)manager
didExitRegion:(CLBeaconRegion *)region {
NSDate *now = [NSDate date];
NSDictionary *regionDict = [self convertBeaconRegionToDict: region];
NSDictionary *event = @{
@"region": regionDict,
@"type": @"didExit",
@"date": [NSNumber numberWithUnsignedInteger:now.timeIntervalSince1970]
};
[[NSUserDefaults standardUserDefaults] setObject:event forKey:regionDict[@"uuid"]];
[[NSUserDefaults standardUserDefaults] synchronize];
[self sendEventWithName:@"regionDidExit" body:event];
}
+ (BOOL)requiresMainQueueSetup
{
return YES;
}
- (NSNumber*)calculateDistance:(NSNumber*)txPower rssi:(NSNumber*) rssi {
if ([rssi floatValue] >= 0){
return [NSNumber numberWithInt:-1];
}
float ratio = [rssi floatValue] / ([txPower floatValue] - 41);
if (ratio < 1.0) {
return [NSNumber numberWithFloat:pow(ratio, 10)];
}
float distance = (0.89976) * pow(ratio, 7.7095) + 0.111;
return [NSNumber numberWithFloat:distance];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment