Skip to content

Commit edef138

Browse files
committed
Merge branch 'refactor-forEachAppDelegateClass-ios' of https://github.com/firebase/firebase-cpp-sdk into refactor-forEachAppDelegateClass-ios
2 parents f49476d + 8071976 commit edef138

File tree

2 files changed

+183
-68
lines changed

2 files changed

+183
-68
lines changed

app/src/util_ios.mm

Lines changed: 151 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@
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

197272
void 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

Comments
 (0)