File tree

1 file changed

+76
-29
lines changed

1 file changed

+76
-29
lines changed
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,70 @@
2727
#import <UIKit/UIKit.h>
2828
#import <objc/runtime.h>
2929

30+
static IMP g_original_setDelegate_imp = NULL;
31+
static Class g_app_delegate_class = nil;
32+
static void (^g_pending_app_delegate_block)(Class) = nil; // New
33+
34+
#include "app/src/log.h" // For LogDebug
35+
36+
// Swizzled implementation of setDelegate:
37+
static void Firebase_setDelegate(id self, SEL _cmd, id<UIApplicationDelegate> delegate) {
38+
if (delegate) {
39+
g_app_delegate_class = [delegate class];
40+
firebase::LogDebug("Firebase: UIApplication setDelegate: called with class %s (Swizzled)",
41+
class_getName(g_app_delegate_class));
42+
} else {
43+
g_app_delegate_class = nil;
44+
firebase::LogDebug("Firebase: UIApplication setDelegate: called with nil delegate (Swizzled)");
45+
}
46+
47+
// New part to add:
48+
if (g_pending_app_delegate_block && g_app_delegate_class) {
49+
firebase::LogDebug("Firebase: Firebase_setDelegate executing pending block with delegate class: %s.",
50+
class_getName(g_app_delegate_class));
51+
g_pending_app_delegate_block(g_app_delegate_class);
52+
g_pending_app_delegate_block = nil; // Clear the block after execution (ARC handles release)
53+
} else if (g_pending_app_delegate_block && !g_app_delegate_class) {
54+
// This case: setDelegate was called with nil, but a block was pending.
55+
// The pending block expects a Class. We don't have one.
56+
// So, we should clear the pending block as it can no longer be satisfied.
57+
firebase::LogDebug("Firebase: Firebase_setDelegate called with nil delegate, clearing pending block as it cannot be executed.");
58+
g_pending_app_delegate_block = nil;
59+
}
60+
61+
if (g_original_setDelegate_imp) {
62+
((void (*)(id, SEL, id<UIApplicationDelegate>))g_original_setDelegate_imp)(self, _cmd, delegate);
63+
} else {
64+
firebase::LogError("Firebase: Original setDelegate: IMP not found, cannot call original method.");
65+
}
66+
}
67+
68+
@implementation UIApplication (FirebaseAppDelegateSwizzling)
69+
70+
+ (void)load {
71+
static dis_once_t onceToken;
72+
dis_once(&onceToken, ^{
73+
Class uiApplicationClass = [UIApplication class];
74+
SEL originalSelector = @selector(setDelegate:);
75+
Method originalMethod = class_getInstanceMethod(uiApplicationClass, originalSelector);
76+
77+
if (!originalMethod) {
78+
firebase::LogError("Firebase: Original [UIApplication setDelegate:] method not found for swizzling.");
79+
return;
80+
}
81+
82+
IMP previousImp = method_setImplementation(originalMethod, (IMP)Firebase_setDelegate);
83+
if (previousImp) {
84+
g_original_setDelegate_imp = previousImp;
85+
firebase::LogDebug("Firebase: Successfully swizzled [UIApplication setDelegate:] and stored original IMP.");
86+
} else {
87+
firebase::LogError("Firebase: Swizzled [UIApplication setDelegate:], but original IMP was NULL (or method_setImplementation failed).");
88+
}
89+
});
90+
}
91+
92+
@end
93+
3094
@implementation FIRSAMAppDelegate
3195
- (BOOL)application:(UIApplication *)application
3296
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
@@ -80,37 +144,20 @@ - (BOOL)application:(UIApplication *)application
80144
namespace util {
81145

82146
void ForEachAppDelegateClass(void (^block)(Class)) {
83-
unsigned int number_of_classes;
84-
Class *classes = objc_copyClassList(&number_of_classes);
85-
for (unsigned int i = 0; i < number_of_classes; i++) {
86-
Class clazz = classes[i];
87-
if (class_conformsToProtocol(clazz, @protocol(UIApplicationDelegate))) {
88-
const char *class_name = class_getName(clazz);
89-
bool blacklisted = false;
90-
static const char *kClassNameBlacklist[] = {
91-
// Declared in Firebase Analytics:
92-
// //googlemac/iPhone/Firebase/Analytics/Sources/ApplicationDelegate/
93-
// FIRAAppDelegateProxy.m
94-
"FIRAAppDelegate",
95-
// Declared here.
96-
"FIRSAMAppDelegate"};
97-
for (size_t i = 0; i < FIREBASE_ARRAYSIZE(kClassNameBlacklist); ++i) {
98-
if (strcmp(class_name, kClassNameBlacklist[i]) == 0) {
99-
blacklisted = true;
100-
break;
101-
}
102-
}
103-
if (!blacklisted) {
104-
if (GetLogLevel() <= kLogLevelDebug) {
105-
// Call NSLog directly because we may be in a +load method,
106-
// and C++ classes may not be constructed yet.
107-
NSLog(@"Firebase: Found UIApplicationDelegate class %s", class_name);
108-
}
109-
block(clazz);
110-
}
147+
if (g_app_delegate_class) {
148+
firebase::LogDebug("Firebase: ForEachAppDelegateClass executing with stored delegate class: %s.",
149+
class_getName(g_app_delegate_class));
150+
block(g_app_delegate_class);
151+
// Clear any pending block as we've now executed with a known delegate.
152+
if (g_pending_app_delegate_block) {
153+
g_pending_app_delegate_block = nil; // ARC handles release
111154
}
155+
} else {
156+
firebase::LogDebug("Firebase: ForEachAppDelegateClass - delegate class not yet known. Saving block for later execution.");
157+
// If a block is already pending, the new one replaces it. ARC handles the old one.
158+
// Make sure to copy the block to move it to the heap.
159+
g_pending_app_delegate_block = [block copy];
112160
}
113-
free(classes);
114161
}
115162

116163
NSDictionary *StringMapToNSDictionary(const std::map<std::string, std::string> &string_map) {

0 commit comments

Comments
 (0)