eddieowens / react-native-boundary

Native implementation of geofencing/region monitoring

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Headless event listener not called when app is killed

mathiasmoeller opened this issue · comments

Hey @eddieowens !

When the app is in foreground or background the boundary callbacks work perfectly!
But I am struggling with the Headless part when the app is killed.
In logcat I can see that the event is fired. I see the log message from boundary's index.js file:

ReactNativeJS: 'onExit', [ 'rated0' ]

which comes from this part of code here:

const HeadlessBoundaryEventTask = async ({event, ids}) => {
  console.log(event, ids);
  boundaryEventEmitter.emit(event, ids)
};

But my callback is never executed. I think the issue might be timing.
When the app starts because of a Headless event I can see that boundary receives the event as said above. I added log messages for the Callback Registration and it seems to happen after the Headless event:

15:03:19.837 ReactNativeJS: 'onExit', [ 'rated0' ] // from boundary's index.js
15:03:19.863 ReactNativeJS: Registering Boundary Callbacks // my code

I moved the callback registration to the very first line of my index.js but the order still seems to be incorrect. Any idea how to solve this? Where should I call the Boundary.on callback registration in my code?

Setup:

  • "react-native": "0.59.5"
  • "react-native-activity-recognition": "woffu/react-native-boundary" // fix for newer android versions

Help is very much appreciated :)

@mathiasmoeller I was having the same issue and fixed it by registering another headless task in index.js for the event. Then I just have it run the same code as it normally would. This method doesn't work for iOS, however, so it's not a great solution. I can't get iOS to do stuff when the app is killed and I don't know why.

Hi @NuclearKev , can you share the code how you did that! That will be a big help!

Sure! I use ClojureScript so it'll look kinda weird but you should be able to get the gist:

Somewhere in index.js you'll need to add the headless task register

(.registerHeadlessTask app-registry "OnBoundaryEvent" (fn [] async-boundary))

Make sure that you register the same event that react-native-boundary does, namely "OnBoundaryEvent".

registerHeadlessTask requires that the function you pass it be async. In CLJS I had to have a function that returns the async function but I don't think you need that in JS.

Then, somewhere else:

(defn async-boundary [d]
  (async
   (let [event (.-event ^js d)
         id (first (.-ids ^js d))]
     (when (= event "onEnter")
       ;; run some code in here!
      ))
   (js/Promise.resolve)))

and that's literally it!

EDIT: You may need to comment out the headless task in react-native-boundary to remove some warnings.

@NuclearKev
How would that look like in TypeScript?
Do I just put

AppRegistry.registerHeadlessTask('OnBoundaryEvent', (event) => {
    if(event == "onEnter"){
        //my code here
     }
})

into the index.js?
My main concern is: how would I get access to the id?
Does it get passed along in the event ?

Your answer would be highly appreciated.

@SufniDroid just like how react-native-boundary does it:

const HeadlessBoundaryEventTask = async ({event, ids}) => {
  console.log(event, ids);
  boundaryEventEmitter.emit(event, ids)
};

So in our case:

const HeadlessBoundaryEventTask = async ({event, ids}) => {
     if(event === "onEnter"){
        //my code here
     }
};

EDIT: or like this if you don't want to have a named function:

AppRegistry.registerHeadlessTask('OnBoundaryEvent', async ({event, ids}) => {
    if(event === "onEnter"){
        //my code here
     }
})

I hope that works for you guys! I still have yet to figure out iOS....

I know that the event gets triggered (if I put a breakpoint on the native code where the event gets triggered, it will stop there) but the code in the event listener doesn't get ran. No idea how to make it run. I was thinking it was maybe related to BGTaskScheduler but the more I look into it the less I think that's it.

@NuclearKev I am working on a fix for iOS, should come very shortly.

@cladjules please, keep us posted! :)

Just tested this ability with react-native-background-geolocation and it worked. So it's definitely possible! Their library is far more complicated than react-native-boundary, so getting it to work would be excellent. Not to mention, the other one causes a lot of money to use on Android...

Yes a fix for iOS would be awesome! I managed to get it run on Android (the way @NuclearKev suggested). Now iOS needs to work 😁
Thanks for your help!

I have opened a PR here: #56

It looks ok on my side, if you could do some testing, that would be great.

Thanks

Thanks, @cladjules

I'll be testing that out later today!

@cladjules your fixes worked!

@NuclearKev Great. Thanks for the help on Android as well.
That should probably be documented?

@cladjules where should it be documented?

@NuclearKev How to add the Headless function, it's not obvious until you find that open issue.
In the Readme, I guess.

Does anyone know if there is a way to force iOS to check the location upon app startup? It looks like if the user has the location permission set to "while using," it won't fire the event if they are in the radius when they open the app.

EDIT: I tried adding in a function that runs [self.locationManager checkLocation] but it doesn't seem to work.

The native APIs are designed to work only if the user sets the location permission to always.

@SufniDroid just like how react-native-boundary does it:

const HeadlessBoundaryEventTask = async ({event, ids}) => {
  console.log(event, ids);
  boundaryEventEmitter.emit(event, ids)
};

So in our case:

const HeadlessBoundaryEventTask = async ({event, ids}) => {
     if(event === "onEnter"){
        //my code here
     }
};

EDIT: or like this if you don't want to have a named function:

AppRegistry.registerHeadlessTask('OnBoundaryEvent', async ({event, ids}) => {
    if(event === "onEnter"){
        //my code here
     }
})

@NuclearKev Where can I put this code?
I put it in componentDidMount() but it is not working. There is an error: taskProvider() is not a function. (In 'taskProvider()(data)', 'taskProvider()' is an instance of Promise).

It shoud be like this: AppRegistry.registerHeadlessTask('OnBoundaryEvent', () => async ({event, ids}) => { if(event === "onEnter"){ //my code here } })

@xoapit I believe that in JavaScript you need to place it in the index.js file. (In ClojureScript it can be placed in the core.cljs)

@xoapit @NuclearKev I doubt you need to add anything additional?
It's already included in that lib's index.js: https://github.com/eddieowens/react-native-boundary/blob/master/index.js

Should work out of the box, as long as your listeners are not within a React lifecycle.

on Event doesn't work in Android when the app is killed so I have to use addAppRegistry.registerHeadlessTask outside of React lifecycle as a trick to observe event changes.