Skip to content

feat: delay the launch event until the app becomes active #9906

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

edusperoni
Copy link
Contributor

@edusperoni edusperoni commented May 11, 2022

This is a draft/discussion PR.

According to the iOS docs, the UI should be built on didBecomeActive instead of didFinishLaunchingWithOptions. This makes a difference when we launch the application due to a fetch event or firebase message, as didFinishLaunchingWithOptions will launch with the app in the background and no UI will be shown unless the user opens the app manually in the meantime.

The same scenario on Android delays the launchEvent until the app opens. This PR would make both platforms behave the same.

Checklist:

  • Check if this change introduces a short black screen between the splash and the main screen
  • If the above is true, only delay the launch if we're in a background launch in didFinishLaunchingWithOptions
  • Check if this breaks any existing app (it shouldn't)
  • Maybe provide an iOS only event for intercepting didFinishLaunchingWithOptions

BREAKING CHANGE:
On iOS, the following flow would happen on background launch (app receives a push or is awaked for background fetch):

  1. Receives event
  2. main.ts runs
  3. Application.run is called
  4. launchEvent is fired and UI is created

Now, it follows the same flow as android:

  1. Receives event
  2. main.ts runs
  3. Application.run is called
  4. no event fires, app eventually is suspended/killed by the system
  5. user taps the app icon while the app hasn't been killed yet (or taps a notification that opens the app)
  6. launchEvent is fired and UI is created

This means that receiving a notification is now much more lightweight if the user doesn't open the app immediately

@cla-bot cla-bot bot added the cla: yes label May 11, 2022
@NathanWalker NathanWalker added this to the 9.0 milestone Aug 16, 2022
@edusperoni edusperoni force-pushed the feat/delay-ios-launch branch from 4888e9c to 9a81cd7 Compare July 3, 2023 16:08
@edusperoni edusperoni marked this pull request as ready for review July 3, 2023 16:08
@farfromrefug
Copy link
Collaborator

wouldnt it be better to add a new event? for me launched is when native application object is created. that change could break some logic, or make app slower. adding a displayed event would be a good addition

@edusperoni
Copy link
Contributor Author

edusperoni commented Jul 4, 2023

@farfromrefug in Android, the launch event is called when the Activity has launched, not the Application. This would also mean that flavors would have to handle different events for android/ios as well, as currently the launch event is the correct place to set the root view, but on iOS this happens before we even know that the app will need a root view.

So we either add a breaking change that the launch event is now NOT the correct way of creating the UI (and remove args.root from the LaunchEventData), or change the iOS behavior to match the Android side.

So you'd need something like:

if(isIOS) {
  Application.on('displayed', () => createUI and set root view);
} else {
  Application.on('launch', () => createUI and set root view);
}

@farfromrefug
Copy link
Collaborator

@edusperoni i get your point.
I understand it is not exactly the same way right now.
To be clear i was not saying displayed as the place to createUI, but as the event fired when the UI is displayed!
Maybe it means we need 3 events to make things working for all contexts:

  • launch : context/application is created. So any plugin/3rdparty needing to instantiate using an android context or the ios application can use that
  • createUI/somethingelse : event fired to createUI and set root view. Which if i understand correctly is the one you want?
  • displayed: fully displayed app. Can be used to trigger content refresh, or on android calling reportFullyDrawn. BTW just realised that displayed event already exist on iOS and Android. Not 100% sure when exactly this is called on iOS but it is correct on android

@edusperoni
Copy link
Contributor Author

on iOS, application is available from the start (before Application.run). On Android, it's available directly after Application.run (I've talked with @rigor789 and we'll most likely refactor it to make it available from the start as well).

Android has an 'activityCreated' event that you can use to get the startActivity/currentActivity if they're missing.

Currently, this code is inconsistent:

Application.on('launch', () => {
  initializeSomeNativeApi()
}):

As this code won't run on android if it's launched from the background. On android there's also no way of getting the application context without a setTimeout or placing the code after Application.run().

I believe the less disruptive change is to make launch event run delayed on iOS, as if you make it run earlier on Android, it'll break quite a few things that depend on startActivity.

Maybe we can do:

  • initialized: runs on didFinishLaunchingWithOptions on iOS and right after you call Application.run() on android. This event means the application itself has been initialized
  • launch: application has launched to the foreground

Naming is a bit hard. Launch could make more sense as the initial event, but I think it's too disruptive on android, as launch/exit events were always tied to the UI lifecycle (activity). They'd be called multiple times on android 11- if you left the app with the back button, for example. All integrations also use launch/exit for their UI creation/cleanup (apart from maybe vanilla).

This PR doesn't actually change the behavior for non-background apps, though, as didBecomeActive is called right after didFinishLaunchingWithOptions.

@farfromrefug
Copy link
Collaborator

farfromrefug commented Jul 4, 2023

@edusperoni just to point out the current behavior of launch on android when run from the background is totally fine for me. and always was. it is doing what it i think it is supposed to. if the app is in background you dont need to re initialize (like sentry for example)
abd for the rest I like initialized/launch.
but then launch is only the first foreground...

@edusperoni
Copy link
Contributor Author

edusperoni commented Jul 4, 2023

On android 11- the launch event is called multiple times (open app, launch is called, use back button, exit is called, open app again, launch is called). The idea of this PR is to make both iOS and Android behave the same way.

iOS and Android 12+ will never call exit event though, only launch a single time (on android, only if the app has been opened in the foreground, will never be called when receiving a firebase data notification for example)

Edit:
Just to be clear, you really do need to reinitialize when the app is awaken in the background. Otherwise Sentry will only be enabled once you launch the app to the foreground, meaning it'll fail to catch some exception that happened in a background service for example.

@edusperoni
Copy link
Contributor Author

This seems to be causing some confusion. Let me clarify it a bit:

// main.ts

console.log('Starting main.ts');

firebase()
	.messaging()
	.onMessage((remoteMessage) => {
		console.log('received firebaseMessage');
	});

Application.on('launch', (args) => {
  console.log('launch event');
  // initialize some native API
  const rootView = new GridLayout();
  rootView.on('loaded', () => console.log('rootView loaded');
  args.root = rootView;
});

Application.run();

console.log('end main.ts');

Android AND iOS, tapping the app icon to open the app

Starting main.ts
end main.ts
launch event
rootView loaded

Android, receiving a data notification from the background:

Starting main.ts
end main.ts
received firebaseMessage
---------- next part is optional
// user taps the app (before app is killed by the system)
launch event
rootView loaded

iOS, receiving a data notification from the background

Starting main.ts
end main.ts
launch event
received firebaseMessage
---------- next part is optional
// user taps the app (before app is killed by the system)
rootView loaded

If you assume the user never launches the app (which is the case most of the time), then you'll have executed the full launch event without the need to, which includes creating all the views, and most likely doing initial network requests like the app is starting (on angular's case, it bootstraps the app completely, so if you're doing any kind of ngOnInit or using ngrx effects or whatever, it'll all fire).

Proposed change (both platforms):

tapping the app icon to open the app

Starting main.ts
end main.ts
launch event
rootView loaded

background notification

Starting main.ts
end main.ts
received firebaseMessage
---------- next part is optional
// user taps the app (before app is killed by the system)
launch event
rootView loaded

this is a subtle change that will only affect if you're using some kind of background execution on your app where it won't fire a ton of requests on iOS when all you do is receive a push notification.

@farfromrefug
Copy link
Collaborator

@edusperoni ok I understand I think it is the same issue as the loaded event for views. people use it to trigger web request but it is called far too many times in an app life cycle. think it is the same with launch event.

@asharghi
Copy link
Contributor

asharghi commented Jul 4, 2023

Just a question that is semi related, which application event do you guys recommand to init Sentry on?

@edusperoni
Copy link
Contributor Author

On iOS you can initialize right away, I'm initializing after Application.run on Android (or in my case runNativeScriptApp). Soon you should be able to initialize anywhere on Android (once we change so that we don't need application.run to set the native android app)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants