MacKentoch / react-native-beacons-manager

React-Native library for detecting beacons (iOS and Android)
MIT License
579 stars 319 forks source link

App Killed state doesn't scan beacon in react native ? #91

Open kiritm-nimblechapps opened 6 years ago

kiritm-nimblechapps commented 6 years ago

I have use the react-native-beacons-manager library for beacon scan. it working fine in foreground and background. but when I will killed the app that stop to scan beacon.

Please give me solution.

I also ask the question on : https://stackoverflow.com/questions/49787641/app-killed-state-doesnt-scan-beacon-in-react-native

IgorMitev commented 6 years ago

@kiritnim for IOS enable Acts as a Bluetooth LE Accessory in Capabilities -> Background Modes, the detection wasn't working while the screen was locked.

Does anybody have a working app for Android? As @kiritnim said it's working fine for foreground background, but not when the app is killed.

Najeisleem commented 5 years ago

there is any update if the app killed ?

daominhsangvn commented 5 years ago

For iOS, open RNiBeacon.m and add following code to it:

- (instancetype)init
{
  if (self = [super init]) {
     ...
    self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
    self.locationManager.allowsBackgroundLocationUpdates = true;
    ...
  }

  return self;
}

RCT_EXPORT_METHOD(startMonitoringForRegion:(NSDictionary *) dict)
{
  [self.locationManager startMonitoringSignificantLocationChanges]; // Add this
  ...
}

RCT_EXPORT_METHOD(stopMonitoringForRegion:(NSDictionary *) dict)
{
  [self.locationManager stopMonitoringSignificantLocationChanges]; // Add this
  ...
}

-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
    NSLog(@"[Beacon][Native] didUpdateToLocation");
}

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
    NSLog(@"[Beacon][Native] didUpdateLocations");
}

When you kill the app from Task (Double Home), you will need to send a push notification with content_available=true to user device to start the app in background (without UI)

For me, i have modified the AppDelegate.m with following:

- (void)applicationWillTerminate:(UIApplication *)application {
  NSLog(@"[Beacon] The application has been terminated, sending out notification to launch application in background");

  // Get data from AsyncStorage with Persist plugin
  NSDictionary *json = [self jsonFromLocalRNStrogeForKey:@"persist:root"];
  NSString *auth = [json valueForKeyPath:@"auth"];
  auth = [auth substringFromIndex:1]; //
  auth = [auth substringToIndex:[auth length] - 1];
  auth = [auth stringByReplacingOccurrencesOfString:@"\\\""
                                       withString:@"\""];
  NSLog(@"auth %@", auth);
  NSError *authDataError;
  NSDictionary *authJson = [NSJSONSerialization JSONObjectWithData:[auth dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&authDataError];

  if(authDataError == nil) {
    NSString *accessToken = [authJson valueForKeyPath:@"data.accessToken"]; // Get access token
    NSString *userId = [authJson valueForKeyPath:@"data.currentUser.id"]; // Get current user id
    NSString *apiUrl = [ReactNativeConfig envFor:@"API_URL"]; // get domain api from react-native-config
    NSString *notificationTitle = [ReactNativeConfig envFor:@"NotificationTitle"];
    NSString *requestUrl = [NSString stringWithFormat:@"%@/notification", apiUrl];
    NSDictionary *jsonData = [[NSDictionary alloc] initWithObjectsAndKeys:
                              @"true", @"content_available",
                              notificationTitle, @"title",
                              userId, @"userId",
                              @"The app has entered the background", @"message",
                              nil];

    NSError* error;
    NSData* data = [NSJSONSerialization dataWithJSONObject:jsonData options:0 error:&error];
    NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"POST DATA: %@", dataString);
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setHTTPMethod:@"POST"];
    NSLog(@"URL: %@", requestUrl);
    [request setURL:[NSURL URLWithString:requestUrl]];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
    NSString *authorizationToken = [NSString stringWithFormat:@"Bearer %@", accessToken];
    [request setValue:authorizationToken forHTTPHeaderField:@"Authorization"];
    [request setHTTPBody:[dataString dataUsingEncoding:NSUTF8StringEncoding]];
    NSLog(@"Request Headers:");
    for (NSString *header in [request allHTTPHeaderFields])
    {
      NSLog(@"%@: %@", header,[request valueForHTTPHeaderField:header]);
    }

    NSError *responseError;
    NSURLResponse *response = nil;

    sleep(2);

    NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&responseError];
    if (!error) {
      NSLog(@"Response String : %@", [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]);

      id json = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:nil];
      NSLog(@"Parsed Data : %@", json);
    }
  }
}

I have modified the code on my fork, feel free to use it https://github.com/MacKentoch/react-native-beacons-manager or install with npm $ npm install @nois/react-native-beacons-manager

In my fork, i have modified Android code as well to make it start the application automatically when user enter the Beacon Region.

In the react-native side, remember do not remove beacon event on componentWillUnmount, it will stop the beacon from scanning because when you kill the app, the component will be destroyed as well.

react-native code example:

## Android

   constructor(props) {
      super(props);

       this.region = {
         identifier: Config.beaconIdentifier,
         uuid: Config.beaconUUID
       };
     }

    componentDidMount() {
        // Tells the library to detect iBeacons
    Beacons.setForegroundScanPeriod(5000);
    Beacons.setBackgroundScanPeriod(5000);

    Beacons.detectIBeacons();

    this.beaconEventListener = Beacons.BeaconsEventEmitter.addListener('beaconsDidRange', this.beaconsDidRange);

    /*
      Monitoring: actions triggered on entering/exiting region’s range;
      works no matter whether the app is running, suspended, or killed (if the app's not running when an enter/exit even comes,
      iOS will launch it into the background for a few seconds to handle the event)
     */
    Beacons.startMonitoringForRegion(this.region);

    /*
      Ranging: actions triggered based on proximity to a beacon; works only when the app is running
      (e.g., it's displayed on screen, or running in the background in response to a monitoring event, etc.)
     */
    Beacons.startRangingBeaconsInRegion(this.region);
    }
## iOS

   constructor(props) {
      super(props);

       this.region = {
         identifier: Config.beaconIdentifier,
         uuid: Config.beaconUUID
       };
     }

    componentDidMount() {
        // Listen for beacon changes
    this.beaconEventListener = Beacons.BeaconsEventEmitter.addListener(
      'beaconsDidRange',
      this.beaconsDidRange
    );

    this.regionDidEnterListener = Beacons.BeaconsEventEmitter.addListener(
      'regionDidEnter',
      this.regionDidEnter
    );

    this.didDetermineStateListener = Beacons.BeaconsEventEmitter.addListener(
      'didDetermineState',
      this.didDetermineState
    );

    this.authorizationStatusDidChangeListener = Beacons.BeaconsEventEmitter.addListener(
      'authorizationStatusDidChange',
      this.authorizationStatusDidChange
    );

    Beacons.requestAlwaysAuthorization();

    Beacons.allowsBackgroundLocationUpdates(true);

    /*
      Monitoring: actions triggered on entering/exiting region’s range;
      works no matter whether the app is running, suspended, or killed (if the app's not running when an enter/exit even comes,
      iOS will launch it into the background for a few seconds to handle the event)
     */
    Beacons.startMonitoringForRegion(this.region);

    /*
      Ranging: actions triggered based on proximity to a beacon; works only when the app is running
      (e.g., it's displayed on screen, or running in the background in response to a monitoring event, etc.)
     */
    Beacons.startRangingBeaconsInRegion(this.region);

    Beacons.startUpdatingLocation();
    }

Hope this helps someone.

Najeisleem commented 5 years ago

@daominhsangvn

in android i just remove the componentWillUnmount and it will work normally after app killed?

sorry for bad english

daominhsangvn commented 5 years ago

@daominhsangvn

in android i just remove the componentWillUnmount and it will work normally after app killed?

sorry for bad english

Not really, you will need to use the RegionBootstrap as well. Check this https://altbeacon.github.io/android-beacon-library/resume-after-terminate.html

Najeisleem commented 5 years ago

@daominhsangvn in android i just remove the componentWillUnmount and it will work normally after app killed? sorry for bad english

Not really, you will need to use the RegionBootstrap as well. Check this https://altbeacon.github.io/android-beacon-library/resume-after-terminate.html

@daominhsangvn how to use RegionBootstrap in react native or in this plugin ? i didnt understand ?

Sorry again

Najeisleem commented 5 years ago

@daominhsangvn i could not find your fork

daominhsangvn commented 5 years ago

@daominhsangvn in android i just remove the componentWillUnmount and it will work normally after app killed? sorry for bad english

Not really, you will need to use the RegionBootstrap as well. Check this https://altbeacon.github.io/android-beacon-library/resume-after-terminate.html

@daominhsangvn how to use RegionBootstrap in react native or in this plugin ? i didnt understand ?

Sorry again

You need to use it inside the plugin.

daominhsangvn commented 5 years ago

@daominhsangvn i could not find your fork

Here the Github: https://github.com/newoceaninfosys/react-native-beacons-manager Npm: https://www.npmjs.com/package/@nois/react-native-beacons-manager

e-nouri commented 4 years ago

@daominhsangvn Thank you so much for the fork, I was going through the commits you made, and i come across the REST API ping with the location and notification when the app in a killed state, I was wondering If i can ask you some questions, my discord is @e-nouri, I also have wechat if you prefer, my id is Nnouri

RM-Eric commented 4 years ago

@daominhsangvn I tried your fork on iOS, but it seems to be crashing the app immediately

Also, in your example above, what is this.didDetermineState supposed to be doing?

Also, do your changes to AppDelegate.m have any peer dependencies? I was getting some errors for react native config and NSJsonSerialization (even though I tried installing react-native-config)

organom commented 4 years ago

@daominhsangvn i could not find your fork

Here the Github: https://github.com/newoceaninfosys/react-native-beacons-manager Npm: https://www.npmjs.com/package/@nois/react-native-beacons-manager

Crashes on app start :'( Why not submit a pull request?