2727#import < UIKit/UIKit.h>
2828#import < objc/runtime.h>
2929
30+ using firebase::GetLogLevel;
31+
32+ // Key used in Info.plist to specify a custom AppDelegate class name.
33+ static NSString *const kFirebaseAppDelegateClassNameKey = @" FirebaseAppDelegateClassName" ;
34+
3035#define MAX_PENDING_APP_DELEGATE_BLOCKS 8
3136#define MAX_SEEN_DELEGATE_CLASSES 32
3237
@@ -40,10 +45,12 @@ static void Firebase_setDelegate(id self, SEL _cmd, id<UIApplicationDelegate> de
4045 Class new_class = nil ;
4146 if (delegate) {
4247 new_class = [delegate class ];
43- NSLog (@" Firebase: UIApplication setDelegate: called with class %s (Swizzled)" ,
44- class_getName (new_class));
48+ if (GetLogLevel () <= kLogLevelDebug )
49+ NSLog (@" Firebase: UIApplication setDelegate: called with class %s (Swizzled)" ,
50+ class_getName (new_class));
4551 } else {
46- NSLog (@" Firebase: UIApplication setDelegate: called with nil delegate (Swizzled)" );
52+ if (GetLogLevel () <= kLogLevelDebug )
53+ NSLog (@" Firebase: UIApplication setDelegate: called with nil delegate (Swizzled)" );
4754 }
4855
4956 if (new_class) {
@@ -54,8 +61,10 @@ static void Firebase_setDelegate(id self, SEL _cmd, id<UIApplicationDelegate> de
5461 for (int i = 0 ; i < g_seen_delegate_classes_count; i++) {
5562 if (g_seen_delegate_classes[i] == current_super) {
5663 superclass_already_seen = true ;
57- NSLog (@" Firebase: Delegate class %s has superclass %s which was already seen. Skipping processing for %s ." ,
58- class_getName (new_class), class_getName(current_super), class_getName(new_class));
64+ if (GetLogLevel () <= kLogLevelDebug )
65+ NSLog (@" Firebase: Delegate class %s has superclass %s which was already seen. Skipping "
66+ @" processing for %s ." ,
67+ class_getName (new_class), class_getName(current_super), class_getName(new_class));
5968 break ;
6069 }
6170 }
@@ -69,8 +78,9 @@ static void Firebase_setDelegate(id self, SEL _cmd, id<UIApplicationDelegate> de
6978 for (int i = 0 ; i < g_seen_delegate_classes_count; i++) {
7079 if (g_seen_delegate_classes[i] == new_class) {
7180 direct_class_already_seen = true ;
72- NSLog (@" Firebase: Delegate class %s already seen directly. Skipping processing." ,
73- class_getName (new_class));
81+ if (GetLogLevel () <= kLogLevelDebug )
82+ NSLog (@" Firebase: Delegate class %s already seen directly. Skipping processing." ,
83+ class_getName (new_class));
7484 break ;
7585 }
7686 }
@@ -80,12 +90,14 @@ static void Firebase_setDelegate(id self, SEL _cmd, id<UIApplicationDelegate> de
8090 if (g_seen_delegate_classes_count < MAX_SEEN_DELEGATE_CLASSES) {
8191 g_seen_delegate_classes[g_seen_delegate_classes_count] = new_class;
8292 g_seen_delegate_classes_count++;
83- NSLog (@" Firebase: Added new delegate class %s to seen list (total seen: %d )." ,
84- class_getName (new_class), g_seen_delegate_classes_count);
93+ if (GetLogLevel () <= kLogLevelDebug )
94+ NSLog (@" Firebase: Added new delegate class %s to seen list (total seen: %d )." ,
95+ class_getName (new_class), g_seen_delegate_classes_count);
8596
8697 if (g_pending_block_count > 0 ) {
87- NSLog (@" Firebase: Executing %d pending block(s) for new delegate class: %s ." ,
88- g_pending_block_count, class_getName (new_class));
98+ if (GetLogLevel () <= kLogLevelDebug )
99+ NSLog (@" Firebase: Executing %d pending block(s) for new delegate class: %s ." ,
100+ g_pending_block_count, class_getName (new_class));
89101 for (int i = 0 ; i < g_pending_block_count; i++) {
90102 if (g_pending_app_delegate_blocks[i]) {
91103 g_pending_app_delegate_blocks[i](new_class);
@@ -94,7 +106,8 @@ static void Firebase_setDelegate(id self, SEL _cmd, id<UIApplicationDelegate> de
94106 }
95107 }
96108 } else {
97- NSLog (@" Firebase Error: Exceeded MAX_SEEN_DELEGATE_CLASSES (%d ). Cannot add new delegate class %s or run pending blocks for it." ,
109+ NSLog (@" Firebase Error: Exceeded MAX_SEEN_DELEGATE_CLASSES (%d ). Cannot add new delegate "
110+ @" class %s or run pending blocks for it." ,
98111 MAX_SEEN_DELEGATE_CLASSES, class_getName (new_class));
99112 }
100113 }
@@ -103,7 +116,8 @@ static void Firebase_setDelegate(id self, SEL _cmd, id<UIApplicationDelegate> de
103116
104117 // Call the original setDelegate: implementation
105118 if (g_original_setDelegate_imp) {
106- ((void (*)(id , SEL , id <UIApplicationDelegate>))g_original_setDelegate_imp)(self, _cmd, delegate);
119+ ((void (*)(id , SEL , id <UIApplicationDelegate>))g_original_setDelegate_imp)(self, _cmd,
120+ delegate);
107121 } else {
108122 NSLog (@" Firebase Error: Original setDelegate: IMP not found, cannot call original method." );
109123 }
@@ -163,28 +177,89 @@ @implementation UIApplication (FirebaseAppDelegateSwizzling)
163177+ (void )load {
164178 static dispatch_once_t onceToken;
165179 dispatch_once (&onceToken, ^{
180+ NSString *appDelegateClassName =
181+ [[NSBundle mainBundle ] objectForInfoDictionaryKey: kFirebaseAppDelegateClassNameKey ];
182+
183+ if (appDelegateClassName && [appDelegateClassName isKindOfClass: [NSString class ]] &&
184+ appDelegateClassName.length > 0 ) {
185+ Class specificClass = NSClassFromString (appDelegateClassName);
186+ if (specificClass) {
187+ if (GetLogLevel () <= kLogLevelDebug )
188+ NSLog (@" Firebase: Info.plist key '%@ ' found. Targeting AppDelegate class: %@ . Swizzling "
189+ @" of [UIApplication setDelegate:] will be skipped." ,
190+ kFirebaseAppDelegateClassNameKey , appDelegateClassName);
191+
192+ // Set this class as the sole "seen" delegate for Firebase processing.
193+ // g_seen_delegate_classes_count should be 0 here in +load, but clear just in case.
194+ for (int i = 0 ; i < g_seen_delegate_classes_count; i++) {
195+ g_seen_delegate_classes[i] = nil ;
196+ }
197+ g_seen_delegate_classes[0 ] = specificClass;
198+ g_seen_delegate_classes_count = 1 ;
199+ if (GetLogLevel () <= kLogLevelDebug )
200+ NSLog (@" Firebase: %@ is now the only delegate class Firebase will initially process." ,
201+ appDelegateClassName);
202+
203+ // If there are already blocks pending (e.g., from other Firebase components' +load
204+ // methods), execute them now for the specified delegate. These blocks will remain in the
205+ // pending queue, mirroring the behavior of the original swizzled setDelegate: method which
206+ // also does not clear pending blocks after execution (as they might apply to future
207+ // delegates). In this Info.plist mode, however, Firebase won't process further setDelegate:
208+ // calls.
209+ if (g_pending_block_count > 0 ) {
210+ if (GetLogLevel () <= kLogLevelDebug )
211+ NSLog (@" Firebase: +load (Info.plist Mode) - Executing %d PENDING block(s) for "
212+ @" specified delegate: %@ . (Blocks are not removed from queue)." ,
213+ g_pending_block_count, NSStringFromClass (specificClass));
214+ for (int i = 0 ; i < g_pending_block_count; i++) {
215+ if (g_pending_app_delegate_blocks[i]) {
216+ g_pending_app_delegate_blocks[i](specificClass);
217+ }
218+ }
219+ if (GetLogLevel () <= kLogLevelDebug )
220+ NSLog (@" Firebase: +load (Info.plist Mode) - Pending blocks executed for specific "
221+ @" delegate." );
222+ }
223+ // Skip swizzling. g_original_setDelegate_imp remains NULL.
224+ return ;
225+ } else {
226+ NSLog (@" Firebase Error: Info.plist key '%@ ' specifies class '%@ ', which was not found. "
227+ @" Proceeding with default swizzling." ,
228+ kFirebaseAppDelegateClassNameKey , appDelegateClassName);
229+ }
230+ } else {
231+ if (appDelegateClassName) { // Key is present but value is invalid (e.g., empty string or
232+ // wrong type).
233+ NSLog (@" Firebase Error: Info.plist key '%@ ' has an invalid value ('%@ '). Proceeding "
234+ @" with default swizzling." ,
235+ kFirebaseAppDelegateClassNameKey , appDelegateClassName);
236+ } else { // Key is not present.
237+ // This is the default case, no special logging needed here beyond the swizzling log itself.
238+ }
239+ }
240+
241+ // Standard behavior: Swizzle [UIApplication setDelegate:]
242+ if (GetLogLevel () <= kLogLevelDebug )
243+ NSLog (@" Firebase: Proceeding with swizzling of [UIApplication setDelegate:]." );
166244 Class uiApplicationClass = [UIApplication class ];
167245 SEL originalSelector = @selector (setDelegate: );
168246 Method originalMethod = class_getInstanceMethod (uiApplicationClass, originalSelector);
169247
170248 if (!originalMethod) {
171- NSLog (@" Firebase Error: Original [UIApplication setDelegate:] method not found for swizzling." );
249+ NSLog (
250+ @" Firebase Error: Original [UIApplication setDelegate:] method not found for swizzling." );
172251 return ;
173252 }
174253
175- // Replace the original method's implementation with Firebase_setDelegate
176- // and store the original IMP.
177254 IMP previousImp = method_setImplementation (originalMethod, (IMP )Firebase_setDelegate);
178255 if (previousImp) {
179- g_original_setDelegate_imp = previousImp;
180- NSLog (@" Firebase: Successfully swizzled [UIApplication setDelegate:] and stored original IMP." );
256+ g_original_setDelegate_imp = previousImp;
257+ if (GetLogLevel () <= kLogLevelDebug )
258+ NSLog (@" Firebase: Successfully swizzled [UIApplication setDelegate:] and stored original "
259+ @" IMP." );
181260 } else {
182- // This would be unusual - method_setImplementation replacing a NULL IMP,
183- // or method_setImplementation itself failed (though it doesn't typically return NULL on failure,
184- // it might return the new IMP or the old one depending on versions/runtime).
185- // More robustly, g_original_setDelegate_imp should be checked before use.
186- // For now, this logging indicates if previousImp was unexpectedly nil.
187- NSLog (@" Firebase Error: Swizzled [UIApplication setDelegate:], but original IMP was NULL (or method_setImplementation failed to return the previous IMP)." );
261+ NSLog (@" Firebase Error: Swizzled [UIApplication setDelegate:], but original IMP was NULL (or "
262+ @" method_setImplementation failed to return the previous IMP)." );
188263 }
189264 });
190265}
@@ -196,24 +271,33 @@ + (void)load {
196271
197272void RunOnAppDelegateClasses (void (^block)(Class )) {
198273 if (g_seen_delegate_classes_count > 0 ) {
199- NSLog (@" Firebase: RunOnAppDelegateClasses executing block for %d already seen delegate class(es)." ,
200- g_seen_delegate_classes_count);
274+ if (GetLogLevel () <= kLogLevelDebug )
275+ NSLog (@" Firebase: RunOnAppDelegateClasses executing block for %d already seen delegate "
276+ @" class(es)." ,
277+ g_seen_delegate_classes_count);
201278 for (int i = 0 ; i < g_seen_delegate_classes_count; i++) {
202- if (g_seen_delegate_classes[i]) { // Safety check
279+ if (g_seen_delegate_classes[i]) { // Safety check
203280 block (g_seen_delegate_classes[i]);
204281 }
205282 }
206283 } else {
207- NSLog (@" Firebase: RunOnAppDelegateClasses - no delegate classes seen yet. Block will be queued for future delegates." );
284+ if (GetLogLevel () <= kLogLevelDebug )
285+ NSLog (@" Firebase: RunOnAppDelegateClasses - no delegate classes seen yet. Block will be "
286+ @" queued for future delegates." );
208287 }
209288
210289 // Always try to queue the block for any future new delegate classes.
211290 if (g_pending_block_count < MAX_PENDING_APP_DELEGATE_BLOCKS) {
212291 g_pending_app_delegate_blocks[g_pending_block_count] = [block copy ];
213292 g_pending_block_count++;
214- NSLog (@" Firebase: RunOnAppDelegateClasses - added block to pending list (total pending: %d ). This block will run on future new delegate classes." , g_pending_block_count);
293+ if (GetLogLevel () <= kLogLevelDebug )
294+ NSLog (@" Firebase: RunOnAppDelegateClasses - added block to pending list (total pending: %d ). "
295+ @" This block will run on future new delegate classes." ,
296+ g_pending_block_count);
215297 } else {
216- NSLog (@" Firebase Error: RunOnAppDelegateClasses - pending block queue is full (max %d ). Cannot add new block for future execution. Discarding block." , MAX_PENDING_APP_DELEGATE_BLOCKS);
298+ NSLog (@" Firebase Error: RunOnAppDelegateClasses - pending block queue is full (max %d ). Cannot "
299+ @" add new block for future execution. Discarding block." ,
300+ MAX_PENDING_APP_DELEGATE_BLOCKS);
217301 }
218302}
219303
@@ -458,24 +542,26 @@ void RunOnBackgroundThread(void (*function_ptr)(void *function_data), void *func
458542 const char *class_name = class_getName (clazz);
459543 Method method = class_getInstanceMethod (clazz, name);
460544 NSString *selector_name_nsstring = NSStringFromSelector (name);
461- const char *selector_name = selector_name_nsstring.UTF8String ; // Used for logging later
545+ const char *selector_name = selector_name_nsstring.UTF8String ; // Used for logging later
462546
463547 IMP current_actual_imp = method ? method_getImplementation (method) : nil ;
464548
465549 // === Begin idempotency check ===
466550 if (current_actual_imp == imp) {
467551 // Assuming GetLogLevel() and kLogLevelDebug are available here.
468552 // Based on previous file content, GetLogLevel is available in this file from util_ios.h.
469- if (GetLogLevel () <= kLogLevelDebug ) {
470- NSLog (@" Firebase Cache: Method %s on class %s is already swizzled with the target IMP. Skipping re-swizzle." ,
553+ if (GetLogLevel () <= kLogLevelDebug )
554+ NSLog (@" Firebase: Method %s on class %s is already swizzled with the target IMP. Skipping "
555+ @" re-swizzle." ,
471556 selector_name, class_name);
472- }
473- return ; // Already swizzled to the desired implementation
557+
558+ return ; // Already swizzled to the desired implementation
474559 }
475560 // === End idempotency check ===
476561
477562 // If we reach here, current_actual_imp is different from imp, or the method didn't exist.
478- // We now assign original_method_implementation to be current_actual_imp for the rest of the function.
563+ // We now assign original_method_implementation to be current_actual_imp for the rest of the
564+ // function.
479565 IMP original_method_implementation = current_actual_imp;
480566
481567 // Get the type encoding of the selector from a type_encoding_class (which is a class which
@@ -485,9 +571,9 @@ void RunOnBackgroundThread(void (*function_ptr)(void *function_data), void *func
485571 assert (type_encoding);
486572
487573 NSString *new_method_name_nsstring = nil ;
488- if (GetLogLevel () <= kLogLevelDebug ) {
489- NSLog (@" Firebase Cache : Attempting to register method for %s selector %s " , class_name, selector_name);
490- }
574+ if (GetLogLevel () <= kLogLevelDebug )
575+ NSLog (@" Firebase: Attempting to register method for %s selector %s " , class_name, selector_name);
576+
491577 if (original_method_implementation) {
492578 // Try adding a method with randomized prefix on the name.
493579 int retry = kRandomNameGenerationRetries ;
@@ -502,32 +588,32 @@ void RunOnBackgroundThread(void (*function_ptr)(void *function_data), void *func
502588 }
503589 const char *new_method_name = new_method_name_nsstring.UTF8String ;
504590 if (retry == 0 ) {
505- NSLog (@" Failed to add method %s on class %s as the %s method already exists on the class. To "
506- @" resolve this issue, change the name of the method %s on the class %s ." ,
507- new_method_name, class_name, new_method_name, new_method_name, class_name);
591+ NSLog (
592+ @" Firebase Error: Failed to add method %s on class %s as the %s method already exists on "
593+ @" the class. To resolve this issue, change the name of the method %s on the class %s ." ,
594+ new_method_name, class_name, new_method_name, new_method_name, class_name);
508595 return ;
509596 }
510597 method_setImplementation (method, imp);
511598 // Save the selector name that points at the original method implementation.
512599 SetMethod (name, new_method_name_nsstring);
513- if (GetLogLevel () <= kLogLevelDebug ) {
514- NSLog (@" Registered method for %s selector %s (original method %s 0x%08x )" , class_name ,
515- selector_name, new_method_name,
600+ if (GetLogLevel () <= kLogLevelDebug )
601+ NSLog (@" Firebase: Registered method for %s selector %s (original method %s 0x%08x )" ,
602+ class_name, selector_name, new_method_name,
516603 static_cast <int >(reinterpret_cast <intptr_t >(original_method_implementation)));
517- }
604+
518605 } else if (add_method) {
519- if (GetLogLevel () <= kLogLevelDebug ) {
520- NSLog (@" Adding method for %s selector %s " , class_name, selector_name);
521- }
606+ if (GetLogLevel () <= kLogLevelDebug )
607+ NSLog (@" Firebase: Adding method for %s selector %s " , class_name, selector_name);
608+
522609 // The class doesn't implement the selector so simply install our method implementation.
523610 if (!class_addMethod (clazz, name, imp, type_encoding)) {
524- NSLog (@" Failed to add new method %s on class %s ." , selector_name, class_name);
611+ NSLog (@" Firebase Error: Failed to add new method %s on class %s ." , selector_name, class_name);
525612 }
526613 } else {
527- if (GetLogLevel () <= kLogLevelDebug ) {
528- NSLog (@" Method implementation for %s selector %s not found, ignoring." , class_name,
614+ if (GetLogLevel () <= kLogLevelDebug )
615+ NSLog (@" Firebase: Method implementation for %s selector %s not found, ignoring." , class_name,
529616 selector_name);
530- }
531617 }
532618}
533619
@@ -541,9 +627,9 @@ void RunOnBackgroundThread(void (*function_ptr)(void *function_data), void *func
541627 selector_implementation_names_per_selector_[selector_name_nsstring];
542628 const char *class_name = class_getName (clazz);
543629 if (!selector_implementation_names) {
544- if (GetLogLevel () <= kLogLevelDebug ) {
545- NSLog (@" Method not cached for class %s selector %s ." , class_name, selector_name);
546- }
630+ if (GetLogLevel () <= kLogLevelDebug )
631+ NSLog (@" Firebase: Method not cached for class %s selector %s ." , class_name, selector_name);
632+
547633 return nil ;
548634 }
549635
@@ -561,29 +647,29 @@ void RunOnBackgroundThread(void (*function_ptr)(void *function_data), void *func
561647 search_class = clazz;
562648 for (; search_class; search_class = class_getSuperclass (search_class)) {
563649 const char *search_class_name = class_getName (search_class);
564- if (GetLogLevel () <= kLogLevelDebug ) {
565- NSLog (@" Searching for selector %s (%s ) on class %s " , selector_name,
650+ if (GetLogLevel () <= kLogLevelDebug )
651+ NSLog (@" Firebase: Searching for selector %s (%s ) on class %s " , selector_name,
566652 selector_implementation_name, search_class_name);
567- }
653+
568654 Method method = class_getInstanceMethod (search_class, selector_implementation);
569655 method_implementation = method ? method_getImplementation (method) : nil ;
570656 if (method_implementation) break ;
571657 }
572658 if (method_implementation) break ;
573659 }
574660 if (!method_implementation) {
575- if (GetLogLevel () <= kLogLevelDebug ) {
576- NSLog (@" Class %s does not respond to selector %s (%s )" , class_name, selector_name,
661+ if (GetLogLevel () <= kLogLevelDebug )
662+ NSLog (@" Firebase: Class %s does not respond to selector %s (%s )" , class_name, selector_name,
577663 selector_implementation_name_nsstring.UTF8String);
578- }
664+
579665 return nil ;
580666 }
581- if (GetLogLevel () <= kLogLevelDebug ) {
582- NSLog (@" Found %s (%s , 0x%08x ) on class %s (%s )" , selector_name,
667+ if (GetLogLevel () <= kLogLevelDebug )
668+ NSLog (@" Firebase: Found %s (%s , 0x%08x ) on class %s (%s )" , selector_name,
583669 selector_implementation_name_nsstring.UTF8String,
584670 static_cast <int >(reinterpret_cast <intptr_t >(method_implementation)), class_name,
585671 class_getName(search_class));
586- }
672+
587673 return method_implementation;
588674}
589675
0 commit comments