react-native-ibeacon icon indicating copy to clipboard operation
react-native-ibeacon copied to clipboard

iOS - Background mode doesn´t work

Open marseca opened this issue 8 years ago • 13 comments

Version

0.6.0

Hi!

I am trying that my App run this console.log() when he finds the beacon. It works until I change to Background Mode. Then, the logs stops to show the beacons.

I set the background modes like the tutorial says: Location updates, Use Bluetooth LE accesories and I added Remote notifications ( I want the App send you a Local Notification when it finds a beacon)

My code:

class BeaconManager {
    static f_ids = []; // Beacon identifiers that we have found
    static region = {
        identifier: 'eBeacon',
        uuid: '6269736F-6E74-6500-0000-000000000000'
    };

    static Start() {
        // Request for authorization
        Beacons.requestAlwaysAuthorization();

        Beacons.startMonitoringForRegion(this.region);
        Beacons.startRangingBeaconsInRegion(this.region);

        Beacons.startUpdatingLocation();

        // Listen for beacon changes
        DeviceEventEmitter.addListener(
          'beaconsDidRange',
          (data) => {
           console.log(data); // It works until change to Background mode
          }
        );
    }
}

Steps to reproduce

  1. iOS 9.2.1
  2. iPhone 6
  3. RN 0.18

marseca avatar Feb 29 '16 12:02 marseca

There is an issue with iOS 9 compatibility right now and I'm not sure that this might be a side effect. Let me see if I can reproduce the issue on my end and get back to you in a bit.

frostney avatar Feb 29 '16 13:02 frostney

In iOS 9, there is a newly added property called "allowsBackgroundLocationUpdates" in CLLocationManager class. And it's default vaule is "NO".

So if we want to range beacon in background mode, we have to set this property to "YES", to achieve this, edit the RNBeacon.m file and add these lines below:

if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8) {
    [self.locationManager requestAlwaysAuthorization];
}
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9) {
    self.locationManager.allowsBackgroundLocationUpdates = YES;
}
[self.locationManager startUpdatingLocation];

It should be work fine in iOS 9.

stu60610 avatar Mar 25 '16 10:03 stu60610

@stu60610 Thank you for very much. I'll package that up it into a release very soon.

frostney avatar Mar 25 '16 13:03 frostney

@frostney You're welcome. This is a great module and it helps me a lot!

BTW, If you need more information, here's some links I found to handle this problem:

http://stackoverflow.com/questions/30808192/allowsbackgroundlocationupdates-in-cllocationmanager-in-ios9

https://developer.apple.com/videos/play/wwdc2015/714/

stu60610 avatar Mar 25 '16 13:03 stu60610

@stu60610 Thank you very much! This works like a charm

albo1337 avatar May 10 '16 08:05 albo1337

Does anyone of you guys have some sample code on how you handle background scans:

if ([launchOptions objectForKey:@"UIApplicationLaunchOptionsLocationKey"]) { NSLog(@"DID RECIEVE REGION"); }

Like local notifications or just opening the app

@marseca Can you confirm that @stu60610 suggestion fixed you'r issue. I'm kinda confused on how to log/handle scans in background

felixaa avatar Dec 07 '16 13:12 felixaa

I would also like to know how to handles background scans like @felixaa. In the README.md file there's the following snippet:

 // a region we were scanning for has appeared, ask to open us
  if([launchOptions objectForKey:@"UIApplicationLaunchOptionsLocationKey"])
  {
    //pop a notification to ask user to open, or maybe reload your scanner with delegate so that code fires
  }

I'm assuming this goes in the AppDelegate.m file, so just wondering @frostney or anyone has more details on popping a notification and/or reloading the scanner? Does this have to be done in the appdelegate file in Swift or can I use the react-native notification library?

stuarttayler avatar Dec 12 '16 08:12 stuarttayler

If you want to pop a local notification you can do this.

  if ([launchOptions objectForKey:@"UIApplicationLaunchOptionsLocationKey"]) {
    NSLog(@"DID RECIEVE REGION");
    UILocalNotification *notification = [[UILocalNotification alloc] init];
    notification.alertBody = @"SAH DUDE?!";
    notification.soundName = UILocalNotificationDefaultSoundName;
    
    [[UIApplication sharedApplication] presentLocalNotificationNow:notification];
  }

Btw is it event possible for the JS and 'beaconDidRange' to run in background?

felixaa avatar Dec 13 '16 12:12 felixaa

Thanks @felixaa, that's really helpful.

I guess at that point there's no way to add details about the location event? I was reading on the apple dev site about how you would need to create a CLLocationManager object to get info about the location data. But by adding in more native code it seems to defeat the point of using this library?

That's why I'm wondering if there's a way to run the JS code from the ([launchOptions objectForKey:@"UIApplicationLaunchOptionsLocationKey"]) delegate. Not sure if that's what is implied with the "reload your scanner with delegate so that code fires" comment in the code?

stuarttayler avatar Dec 13 '16 13:12 stuarttayler

I've kinda solved this issue(I think)

You'r AppDelegate.m needs a CLLocationManagerDelegate:

@interface AppDelegate() <CLLocationManagerDelegate>

@property (strong, nonatomic) CLLocationManager *locationManager;

@end

Then in application didFinishLaunchingWithOptions method you need to do this:

  if ([launchOptions objectForKey:@"UIApplicationLaunchOptionsLocationKey"]) {
    NSLog(@"DID RECIEVE REGION");
    
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;
    
    [self.locationManager requestAlwaysAuthorization];
    
    NSUUID *beaconUUID = [[NSUUID alloc] initWithUUIDString:@"YOUR UUID"];
    
     CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:beaconUUID identifier:@"YOUR IDENTIFIER"];
    
    [self.locationManager startMonitoringForRegion:beaconRegion];
  }

Then you can override didEnter/didExit region event-handlers:

-(void)locationManager:(CLLocationManager *)manager
        didEnterRegion:(CLBeaconRegion *)region {
  
  UILocalNotification *notification = [[UILocalNotification alloc] init];
  NSString *alertBody = [@"You've entered region: " stringByAppendingString:region.identifier];
  notification.alertBody = alertBody;
  notification.soundName = UILocalNotificationDefaultSoundName;
  
  [[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}

-(void)locationManager:(CLLocationManager *)manager
         didExitRegion:(CLBeaconRegion *)region {

  UILocalNotification *notification = [[UILocalNotification alloc] init];
  NSString *alertBody = [@"You've exited region: " stringByAppendingString:region.identifier];
  notification.alertBody = alertBody;
  notification.soundName = UILocalNotificationDefaultSoundName;
  
  [[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}

felixaa avatar Dec 28 '16 09:12 felixaa

When i add

 // a region we were scanning for has appeared, ask to open us
  if([launchOptions objectForKey:@"UIApplicationLaunchOptionsLocationKey"])
  {
    //pop a notification to ask user to open, or maybe reload your scanner with delegate so that code fires
  }

And use NSLOG nothing happen :( ! Do you have an idea?

jonathanroze avatar Feb 04 '17 16:02 jonathanroze

@Clowning - I just tried the same thing, did you make any progress on this?

relaxedtomato avatar Mar 20 '17 20:03 relaxedtomato

@Clowning @r-bansal Look at my last comment

felixaa avatar Mar 23 '17 14:03 felixaa