@@ -45,6 +45,7 @@ @implementation FLTFirebaseMessagingPlugin {
4545
4646 // Guard against calling setupNotificationHandling twice
4747 BOOL _notificationHandlingSetup;
48+ BOOL _applicationObserverRegistered;
4849
4950#if TARGET_OS_OSX
5051 // Tracks when plugin registration occurred after the macOS launch notification.
@@ -65,15 +66,39 @@ @implementation FLTFirebaseMessagingPlugin {
6566
6667#pragma mark - FlutterPlugin
6768
68- - (instancetype )initWithFlutterMethodChannel : (FlutterMethodChannel *)channel
69- andFlutterPluginRegistrar : (NSObject <FlutterPluginRegistrar> *)registrar {
69+ - (instancetype )init {
7070 self = [super init ];
7171 if (self) {
7272 _initialNotificationGathered = NO ;
7373 _sceneDidConnect = NO ;
7474 _notificationHandlingSetup = NO ;
75- _channel = channel;
76- _registrar = registrar;
75+ _applicationObserverRegistered = NO ;
76+ }
77+ return self;
78+ }
79+
80+ + (instancetype )sharedInstance {
81+ static FLTFirebaseMessagingPlugin *sharedInstance = nil ;
82+ static dispatch_once_t onceToken;
83+ dispatch_once (&onceToken, ^{
84+ sharedInstance = [[FLTFirebaseMessagingPlugin alloc ] init ];
85+ });
86+ return sharedInstance;
87+ }
88+
89+ + (void )configureNotificationCenterDelegate {
90+ #ifdef __FF_NOTIFICATIONS_SUPPORTED_PLATFORM
91+ [[FLTFirebaseMessagingPlugin sharedInstance ] configureNotificationCenterDelegate ];
92+ #endif
93+ }
94+
95+ - (void )configureWithFlutterMethodChannel : (FlutterMethodChannel *)channel
96+ andFlutterPluginRegistrar : (NSObject <FlutterPluginRegistrar> *)registrar {
97+ _channel = channel;
98+ _registrar = registrar;
99+
100+ if (!_applicationObserverRegistered) {
101+ _applicationObserverRegistered = YES ;
77102 // Application
78103 // Dart -> `getInitialNotification`
79104 // ObjC -> Initialize other delegates & observers
@@ -87,16 +112,14 @@ - (instancetype)initWithFlutterMethodChannel:(FlutterMethodChannel *)channel
87112#endif
88113 object: nil ];
89114 }
90- return self;
91115}
92116
93117+ (void )registerWithRegistrar : (NSObject <FlutterPluginRegistrar> *)registrar {
94118 FlutterMethodChannel *channel =
95119 [FlutterMethodChannel methodChannelWithName: kFLTFirebaseMessagingChannelName
96120 binaryMessenger: [registrar messenger ]];
97- FLTFirebaseMessagingPlugin *instance =
98- [[FLTFirebaseMessagingPlugin alloc ] initWithFlutterMethodChannel: channel
99- andFlutterPluginRegistrar: registrar];
121+ FLTFirebaseMessagingPlugin *instance = [FLTFirebaseMessagingPlugin sharedInstance ];
122+ [instance configureWithFlutterMethodChannel: channel andFlutterPluginRegistrar: registrar];
100123 // Register with internal FlutterFire plugin registry.
101124 [[FLTFirebasePluginRegistry sharedInstance ] registerFirebasePlugin: instance];
102125
@@ -250,6 +273,57 @@ - (void)registerForRemoteNotifications {
250273#endif
251274}
252275
276+ #ifdef __FF_NOTIFICATIONS_SUPPORTED_PLATFORM
277+ - (void )configureNotificationCenterDelegate {
278+ // Set UNUserNotificationCenter but preserve original delegate if necessary.
279+ if (@available (iOS 10.0 , macOS 10.14 , *)) {
280+ BOOL shouldReplaceDelegate = YES ;
281+ UNUserNotificationCenter *notificationCenter =
282+ [UNUserNotificationCenter currentNotificationCenter ];
283+ id <UNUserNotificationCenterDelegate > currentDelegate = notificationCenter.delegate ;
284+
285+ if (currentDelegate == self) {
286+ return ;
287+ }
288+
289+ if (currentDelegate != nil ) {
290+ #if !TARGET_OS_OSX
291+ // If a UNUserNotificationCenterDelegate is set and it conforms to
292+ // FlutterAppLifeCycleProvider then we don't want to replace it on iOS as the earlier
293+ // call to `[_registrar addApplicationDelegate:self];` will automatically delegate calls
294+ // to this plugin. If we replace it, it will cause a stack overflow as our original
295+ // delegate forwarding handler below causes an infinite loop of forwarding. See
296+ // https://github.com/firebasefire/issues/4026.
297+ if ([currentDelegate conformsToProtocol: @protocol (FlutterAppLifeCycleProvider)]) {
298+ // Note this one only executes if Firebase swizzling is **enabled**.
299+ shouldReplaceDelegate = NO ;
300+ }
301+ #endif
302+
303+ if (shouldReplaceDelegate) {
304+ _originalNotificationCenterDelegate = currentDelegate;
305+ _originalNotificationCenterDelegateRespondsTo.openSettingsForNotification =
306+ (unsigned int )[_originalNotificationCenterDelegate
307+ respondsToSelector: @selector (userNotificationCenter:openSettingsForNotification: )];
308+ _originalNotificationCenterDelegateRespondsTo.willPresentNotification =
309+ (unsigned int )[_originalNotificationCenterDelegate
310+ respondsToSelector: @selector (userNotificationCenter:willPresentNotification:
311+ withCompletionHandler: )];
312+ _originalNotificationCenterDelegateRespondsTo.didReceiveNotificationResponse =
313+ (unsigned int )[_originalNotificationCenterDelegate
314+ respondsToSelector: @selector (userNotificationCenter:didReceiveNotificationResponse:
315+ withCompletionHandler: )];
316+ }
317+ }
318+
319+ if (shouldReplaceDelegate) {
320+ __strong FLTFirebasePlugin<UNUserNotificationCenterDelegate> *strongSelf = self;
321+ notificationCenter.delegate = strongSelf;
322+ }
323+ }
324+ }
325+ #endif
326+
253327- (void )setupNotificationHandlingWithRemoteNotification : (nullable NSDictionary *)remoteNotification
254328 actionIdentifier : (nullable NSString *)actionIdentifier {
255329 // If notification handling was already set up (e.g. from
@@ -335,48 +409,10 @@ - (void)setupNotificationHandlingWithRemoteNotification:(nullable NSDictionary *
335409 [_registrar addApplicationDelegate: self ];
336410#endif
337411
338- // Set UNUserNotificationCenter but preserve original delegate if necessary.
339- if (@available (iOS 10.0 , macOS 10.14 , *)) {
340- BOOL shouldReplaceDelegate = YES ;
341- UNUserNotificationCenter *notificationCenter =
342- [UNUserNotificationCenter currentNotificationCenter ];
343-
344- if (notificationCenter.delegate != nil ) {
345- #if !TARGET_OS_OSX
346- // If a UNUserNotificationCenterDelegate is set and it conforms to
347- // FlutterAppLifeCycleProvider then we don't want to replace it on iOS as the earlier
348- // call to `[_registrar addApplicationDelegate:self];` will automatically delegate calls
349- // to this plugin. If we replace it, it will cause a stack overflow as our original
350- // delegate forwarding handler below causes an infinite loop of forwarding. See
351- // https://github.com/firebasefire/issues/4026.
352- if ([notificationCenter.delegate conformsToProtocol: @protocol (FlutterAppLifeCycleProvider)]) {
353- // Note this one only executes if Firebase swizzling is **enabled**.
354- shouldReplaceDelegate = NO ;
355- }
412+ #ifdef __FF_NOTIFICATIONS_SUPPORTED_PLATFORM
413+ [self configureNotificationCenterDelegate ];
356414#endif
357415
358- if (shouldReplaceDelegate) {
359- _originalNotificationCenterDelegate = notificationCenter.delegate ;
360- _originalNotificationCenterDelegateRespondsTo.openSettingsForNotification =
361- (unsigned int )[_originalNotificationCenterDelegate
362- respondsToSelector: @selector (userNotificationCenter:openSettingsForNotification: )];
363- _originalNotificationCenterDelegateRespondsTo.willPresentNotification =
364- (unsigned int )[_originalNotificationCenterDelegate
365- respondsToSelector: @selector (userNotificationCenter:willPresentNotification:
366- withCompletionHandler: )];
367- _originalNotificationCenterDelegateRespondsTo.didReceiveNotificationResponse =
368- (unsigned int )[_originalNotificationCenterDelegate
369- respondsToSelector: @selector (userNotificationCenter:didReceiveNotificationResponse:
370- withCompletionHandler: )];
371- }
372- }
373-
374- if (shouldReplaceDelegate) {
375- __strong FLTFirebasePlugin<UNUserNotificationCenterDelegate> *strongSelf = self;
376- notificationCenter.delegate = strongSelf;
377- }
378- }
379-
380416 // We automatically register for remote notifications as
381417 // application:didReceiveRemoteNotification:fetchCompletionHandler: will not get called unless
382418 // registerForRemoteNotifications is called early on during app initialization, calling this from
0 commit comments