segmentio / analytics-react-native

The hassle-free way to add analytics to your React-Native app.
https://segment.com/docs/sources/mobile/react-native/
MIT License
354 stars 182 forks source link

Track event not sent if app is backgrounded immediately in Android #827

Closed marygarciapepperstone closed 1 year ago

marygarciapepperstone commented 1 year ago

I have a function that sends a track event before the app opens a url in a browser (see STR). This event is sent fine in iOS but on Android it only is sent when the browser is closed and it returned to the app. Basically it delays sending the event when it detects that the app is in background for Android and resumes sending when it is foreground. I would like to know if it is possible to handle this, thanks!

Steps to reproduce Consider this code:

const onClick = async () => {
    track('Button Clicked'); // sends event to analytics
    openAuth0(); // opens browser, puts app to background
}

Expected behavior It should send the event to analytics immediately to both iOS and Android. Actual behavior It only sends the event to iOS immediately, on Android it is delayed and only sends it when app is back in foreground

oscb commented 1 year ago

@marygarciapepperstone this is interesting. I wonder if it is a quirk of RN on Android itself being more strict on lifecycle switches as we don't have any logic to do something in particular when the app goes into fg/bg.

When you say it gets sent immediately does it mean that it goes all the way to Segment? In Android it takes up until the app is in fg again but are the timestamps/data correct?

One option I can think of right away is to try awaiting the track call. It should await all the way til it's processed and ready to send to Segment. It doesn't guarantee that the event will get uploaded because of the flushInterval/flushAt configuration, but it does guarantee that the event is processed and saved to memory. That could help and should be minimal time wait.

Another more complex option would be to have your own FlushPolicy where you trigger a flush right when the app state changes to background, but I'm curious about what's actually going on with RN on Android in this scenario, cause it might not actually work. It is something we probably need to play around with.

Can you share what your client configuration is?

marygarciapepperstone commented 1 year ago

@oscb yes for iOS it sent the data all the way to Segment, while on Android it only was sent once the app was in foreground again but the timestamp was the time when it got sent (before going background) not when the app was foregrounded. Does that make sense?

I tried adding await but the behavior did not change. In addition to the issue, if the app was backgrounded and eventually killed then reopened, the previous data isn't sent at all. I have the same experience similar to this

Here's the client configuration:

const segmentClient = createClient({
  writeKey: '',
  trackAppLifecycleEvents: true,
  debug: true,
  flushInterval: 1,
});
oscb commented 1 year ago

To fix the bug about not sending previous events make sure you install @segment/sovran-react-native 1.0.3. 1.0.2 was causing that bug.

Now going back to the issue here. I was doing some tests and it looks like in Android RN just halts all execution after the app goes into background. If you need to event to get to Segment before that happens I can think of 2 options:

You can trigger a manual flush so that the event gets sent to all destinations:

const onClick = async () => {
    await track('Button Clicked'); // still good idea to await this one first as it guarantees that all your plugins get to process it and enrich it first
    await flush(); // awaits for all destinations to upload the event
    openAuth0(); // opens browser, puts app to background
}

The second option is very similar but might be more sound if this is a common thing throughout your app. Again it involves creating a Flush Policy where you make the client flush every time the app switches from Foreground to Background.

Definitely do upgrade your sovran package to 1.0.3 though, as that will fix the persistence issues.

marygarciapepperstone commented 1 year ago

Thanks for the detailed reply @oscb ! The Android issue was fixed by triggering a manual flush and also adding await to the track method. I did try the manual flush without the await to the track method but it didn't get sent so I had to await both. I appreciate the help 👍