-
Notifications
You must be signed in to change notification settings - Fork 131
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
Enhanced lifecycle-aware module registry #849
base: main
Are you sure you want to change the base?
Conversation
@implementation AppDelegate | ||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { | ||
[[RNLifecycleManager sharedInstance] notifyModulesWithSelector:@selector(applicationDidFinishLaunching:) application:application]; | ||
return YES; | ||
} | ||
@end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps it would be best if this was internal in RN itself (maybe via method swizzling or having some standard interface from core that is exposed and the app delegate implements) this way we can make changes without any user facing breaking API
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My main goal is simply to show, in basic terms, how this works. The actual implementation will occur internally inRCTAppDelegate
, so there’s no need for user-facing changes. I’ll revise the examples to avoid confusion for others.
class MainApplication : Application() { | ||
override fun onCreate() { | ||
super.onCreate() | ||
LifecycleManager.notify { it.onCreate(this) } | ||
} | ||
|
||
override fun onTerminate() { | ||
super.onTerminate() | ||
LifecycleManager.notify { it.onDestroy() } | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for sending this proposal over @matinzd
|
||
While the design details here serve as a conceptual demonstration, the actual implementation may differ based on React Native's evolving architecture. For instance, this could either extend the current module registry or involve introducing a new interface entirely, which may cause breaking changes in some scenarios. These considerations would require careful evaluation during the development phase. | ||
|
||
## Drawbacks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One significant drawbacks that I see here is that it will be harder for brownfield users to integrate a lifecycle aware module inside their apps.
That's because a brownfield app will most likely already have his own onCreate
, onStart
, onResume
implementation and so on. Perhaps on Android it might be using a dependency injection framework to orchestrate those calls.
By implementing this proposals, we'll be hiding away some of the configuration needed for a module to work correctly. So brownfield users will have to discover which modules need an onCreate
callback to work correctly, and call it directly.
As an alternative, we could provide a callback for brownfield users to invoke the LifecycleManager.notify()
methods you mentioned in your examples. Still, this reduces flexibility and makes integration with brownfield apps harder
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would likely have a significant impact on startup performance. A design principle of TurboModules is that modules are created lazily on first access from JS.
We'd need a separate interface marker or API to mark modules as needing lifecycle methods and to allow to delay startup until they're fully initialized.
@interface RNLifecycleManager : NSObject | ||
+ (instancetype)sharedInstance; | ||
- (void)registerModule:(id<RNLifecycleAwareModule>)module; | ||
- (void)notifyModulesWithSelector:(SEL)selector application:(UIApplication *)application; | ||
@end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not be a singleton (neither in iOS or Android).
React Native modules are always instance-scoped. If you reload your application, all your native modules will be re-created, and so any singleton referencing them like this is going to cause a memory leak and other problems.
fun onResume() | ||
fun onPause() | ||
fun onStop() | ||
fun onDestroy() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could invalidate
already be used to provide the same information here?
Right now I don't believe we explicitly hook that up to Application#onDestroy, but we could.
|
||
```kotlin | ||
interface RNLifecycleAwareModule { | ||
fun onCreate(application: Application) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is this different from the module's constructor? On Android, no code runs before the Application object is created anyway.
@protocol RNLifecycleAwareModule <NSObject> | ||
@optional | ||
- (void)applicationDidFinishLaunching:(UIApplication *)application; | ||
- (void)applicationWillResignActive:(UIApplication *)application; | ||
- (void)applicationDidEnterBackground:(UIApplication *)application; | ||
- (void)applicationWillEnterForeground:(UIApplication *)application; | ||
- (void)applicationDidBecomeActive:(UIApplication *)application; | ||
- (void)applicationWillTerminate:(UIApplication *)application; | ||
@end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue I see with this is the migration to UISceneDelegate
. There are a lot of additional lifecycle events. And UISceneDelegate
is the preffered way of handling this from iOS 13+
How would you handle it?
This RFC proposes a framework to enhance the module registry in React Native by introducing a lifecycle-aware system for native modules. The goal is to address the current gap in handling application lifecycle events, similar to Flutter's
FlutterApplicationLifeCycleDelegate
on iOS andApplication.ActivityLifecycleCallbacks
on Android. The design enables seamless integration of native modules with application lifecycle events across iOS and Android platforms. There is also Expo Modules Core that handles this viaExpoAppDelegateSubscriber
andReactActivityLifecycleListener
, but React Native does not have this by default and it requires Expo to be used in such cases.A rendered version of the proposal can be read here