27
27
#import < UIKit/UIKit.h>
28
28
#import < objc/runtime.h>
29
29
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
+
30
35
#define MAX_PENDING_APP_DELEGATE_BLOCKS 8
31
36
#define MAX_SEEN_DELEGATE_CLASSES 32
32
37
@@ -40,10 +45,12 @@ static void Firebase_setDelegate(id self, SEL _cmd, id<UIApplicationDelegate> de
40
45
Class new_class = nil ;
41
46
if (delegate) {
42
47
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));
45
51
} 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)" );
47
54
}
48
55
49
56
if (new_class) {
@@ -54,8 +61,10 @@ static void Firebase_setDelegate(id self, SEL _cmd, id<UIApplicationDelegate> de
54
61
for (int i = 0 ; i < g_seen_delegate_classes_count; i++) {
55
62
if (g_seen_delegate_classes[i] == current_super) {
56
63
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));
59
68
break ;
60
69
}
61
70
}
@@ -69,8 +78,9 @@ static void Firebase_setDelegate(id self, SEL _cmd, id<UIApplicationDelegate> de
69
78
for (int i = 0 ; i < g_seen_delegate_classes_count; i++) {
70
79
if (g_seen_delegate_classes[i] == new_class) {
71
80
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));
74
84
break ;
75
85
}
76
86
}
@@ -80,12 +90,14 @@ static void Firebase_setDelegate(id self, SEL _cmd, id<UIApplicationDelegate> de
80
90
if (g_seen_delegate_classes_count < MAX_SEEN_DELEGATE_CLASSES) {
81
91
g_seen_delegate_classes[g_seen_delegate_classes_count] = new_class;
82
92
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);
85
96
86
97
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));
89
101
for (int i = 0 ; i < g_pending_block_count; i++) {
90
102
if (g_pending_app_delegate_blocks[i]) {
91
103
g_pending_app_delegate_blocks[i](new_class);
@@ -94,7 +106,8 @@ static void Firebase_setDelegate(id self, SEL _cmd, id<UIApplicationDelegate> de
94
106
}
95
107
}
96
108
} 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." ,
98
111
MAX_SEEN_DELEGATE_CLASSES, class_getName (new_class));
99
112
}
100
113
}
@@ -103,7 +116,8 @@ static void Firebase_setDelegate(id self, SEL _cmd, id<UIApplicationDelegate> de
103
116
104
117
// Call the original setDelegate: implementation
105
118
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);
107
121
} else {
108
122
NSLog (@" Firebase Error: Original setDelegate: IMP not found, cannot call original method." );
109
123
}
@@ -163,28 +177,89 @@ @implementation UIApplication (FirebaseAppDelegateSwizzling)
163
177
+ (void )load {
164
178
static dispatch_once_t onceToken;
165
179
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:]." );
166
244
Class uiApplicationClass = [UIApplication class ];
167
245
SEL originalSelector = @selector (setDelegate: );
168
246
Method originalMethod = class_getInstanceMethod (uiApplicationClass, originalSelector);
169
247
170
248
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." );
172
251
return ;
173
252
}
174
253
175
- // Replace the original method's implementation with Firebase_setDelegate
176
- // and store the original IMP.
177
254
IMP previousImp = method_setImplementation (originalMethod, (IMP )Firebase_setDelegate);
178
255
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." );
181
260
} 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)." );
188
263
}
189
264
});
190
265
}
@@ -196,24 +271,33 @@ + (void)load {
196
271
197
272
void RunOnAppDelegateClasses (void (^block)(Class )) {
198
273
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);
201
278
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
203
280
block (g_seen_delegate_classes[i]);
204
281
}
205
282
}
206
283
} 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." );
208
287
}
209
288
210
289
// Always try to queue the block for any future new delegate classes.
211
290
if (g_pending_block_count < MAX_PENDING_APP_DELEGATE_BLOCKS) {
212
291
g_pending_app_delegate_blocks[g_pending_block_count] = [block copy ];
213
292
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);
215
297
} 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);
217
301
}
218
302
}
219
303
@@ -458,24 +542,26 @@ void RunOnBackgroundThread(void (*function_ptr)(void *function_data), void *func
458
542
const char *class_name = class_getName (clazz);
459
543
Method method = class_getInstanceMethod (clazz, name);
460
544
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
462
546
463
547
IMP current_actual_imp = method ? method_getImplementation (method) : nil ;
464
548
465
549
// === Begin idempotency check ===
466
550
if (current_actual_imp == imp) {
467
551
// Assuming GetLogLevel() and kLogLevelDebug are available here.
468
552
// 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." ,
471
556
selector_name, class_name);
472
- }
473
- return ; // Already swizzled to the desired implementation
557
+
558
+ return ; // Already swizzled to the desired implementation
474
559
}
475
560
// === End idempotency check ===
476
561
477
562
// 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.
479
565
IMP original_method_implementation = current_actual_imp;
480
566
481
567
// 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
485
571
assert (type_encoding);
486
572
487
573
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
+
491
577
if (original_method_implementation) {
492
578
// Try adding a method with randomized prefix on the name.
493
579
int retry = kRandomNameGenerationRetries ;
@@ -502,32 +588,32 @@ void RunOnBackgroundThread(void (*function_ptr)(void *function_data), void *func
502
588
}
503
589
const char *new_method_name = new_method_name_nsstring.UTF8String ;
504
590
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);
508
595
return ;
509
596
}
510
597
method_setImplementation (method, imp);
511
598
// Save the selector name that points at the original method implementation.
512
599
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,
516
603
static_cast <int >(reinterpret_cast <intptr_t >(original_method_implementation)));
517
- }
604
+
518
605
} 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
+
522
609
// The class doesn't implement the selector so simply install our method implementation.
523
610
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);
525
612
}
526
613
} 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,
529
616
selector_name);
530
- }
531
617
}
532
618
}
533
619
@@ -541,9 +627,9 @@ void RunOnBackgroundThread(void (*function_ptr)(void *function_data), void *func
541
627
selector_implementation_names_per_selector_[selector_name_nsstring];
542
628
const char *class_name = class_getName (clazz);
543
629
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
+
547
633
return nil ;
548
634
}
549
635
@@ -561,29 +647,29 @@ void RunOnBackgroundThread(void (*function_ptr)(void *function_data), void *func
561
647
search_class = clazz;
562
648
for (; search_class; search_class = class_getSuperclass (search_class)) {
563
649
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,
566
652
selector_implementation_name, search_class_name);
567
- }
653
+
568
654
Method method = class_getInstanceMethod (search_class, selector_implementation);
569
655
method_implementation = method ? method_getImplementation (method) : nil ;
570
656
if (method_implementation) break ;
571
657
}
572
658
if (method_implementation) break ;
573
659
}
574
660
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,
577
663
selector_implementation_name_nsstring.UTF8String);
578
- }
664
+
579
665
return nil ;
580
666
}
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,
583
669
selector_implementation_name_nsstring.UTF8String,
584
670
static_cast <int >(reinterpret_cast <intptr_t >(method_implementation)), class_name,
585
671
class_getName(search_class));
586
- }
672
+
587
673
return method_implementation;
588
674
}
589
675
0 commit comments