Skip to content

Commit 09591c7

Browse files
feat: Support multiple pending ForEachAppDelegateClass blocks
Modified `util_ios.mm` to support queueing multiple blocks (up to 8, defined by `MAX_PENDING_APP_DELEGATE_BLOCKS`) if `ForEachAppDelegateClass` is called before `[UIApplication setDelegate:]` is invoked. Changes include: - Replaced single pending block storage with a C array of block pointers and a counter (`g_pending_app_delegate_blocks` and `g_pending_block_count`). - `ForEachAppDelegateClass` now adds blocks to this array if the app delegate is not yet known. If the array is full, an error is logged and the block is discarded. - `Firebase_setDelegate` (the swizzled method) now iterates through all pending blocks in the array. If a valid delegate is being set, it executes each pending block. If the delegate is being set to nil, it clears all pending blocks. The array count is reset in both cases. - Added `#define MAX_PENDING_APP_DELEGATE_BLOCKS 8` for configurability.
1 parent 0648d55 commit 09591c7

File tree

1 file changed

+42
-21
lines changed

1 file changed

+42
-21
lines changed

app/src/util_ios.mm

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@
2525
#import <UIKit/UIKit.h>
2626
#import <objc/runtime.h>
2727

28+
#define MAX_PENDING_APP_DELEGATE_BLOCKS 8
29+
2830
static IMP g_original_setDelegate_imp = NULL;
2931
static Class g_app_delegate_class = nil;
30-
static void (^g_pending_app_delegate_block)(Class) = nil;
32+
static void (^g_pending_app_delegate_blocks[MAX_PENDING_APP_DELEGATE_BLOCKS])(Class) = {nil};
33+
static int g_pending_block_count = 0;
3134

3235
// Swizzled implementation of setDelegate:
3336
static void Firebase_setDelegate(id self, SEL _cmd, id<UIApplicationDelegate> delegate) {
@@ -40,17 +43,31 @@ static void Firebase_setDelegate(id self, SEL _cmd, id<UIApplicationDelegate> de
4043
NSLog(@"Firebase: UIApplication setDelegate: called with nil delegate (Swizzled)");
4144
}
4245

43-
if (g_pending_app_delegate_block && g_app_delegate_class) {
44-
NSLog(@"Firebase: Firebase_setDelegate executing pending block with delegate class: %s.",
45-
class_getName(g_app_delegate_class));
46-
g_pending_app_delegate_block(g_app_delegate_class);
47-
g_pending_app_delegate_block = nil; // Clear the block after execution (ARC handles release)
48-
} else if (g_pending_app_delegate_block && !g_app_delegate_class) {
49-
// This case: setDelegate was called with nil, but a block was pending.
50-
// The pending block expects a Class. We don't have one.
51-
// So, we should clear the pending block as it can no longer be satisfied.
52-
NSLog(@"Firebase: Firebase_setDelegate called with nil delegate, clearing pending block as it cannot be executed.");
53-
g_pending_app_delegate_block = nil;
46+
// Check and execute/clear g_pending_app_delegate_blocks
47+
if (g_app_delegate_class) { // Delegate is valid, execute pending blocks
48+
if (g_pending_block_count > 0) {
49+
NSLog(@"Firebase: Firebase_setDelegate executing %d pending block(s) with delegate class: %s.",
50+
g_pending_block_count, class_getName(g_app_delegate_class));
51+
for (int i = 0; i < g_pending_block_count; i++) {
52+
if (g_pending_app_delegate_blocks[i]) {
53+
g_pending_app_delegate_blocks[i](g_app_delegate_class);
54+
g_pending_app_delegate_blocks[i] = nil; // Release the block
55+
}
56+
}
57+
// All pending blocks processed, reset count.
58+
g_pending_block_count = 0;
59+
}
60+
} else { // Delegate is nil, clear any pending blocks
61+
if (g_pending_block_count > 0) {
62+
NSLog(@"Firebase: Firebase_setDelegate called with nil delegate, clearing %d pending block(s).", g_pending_block_count);
63+
for (int i = 0; i < g_pending_block_count; i++) {
64+
if (g_pending_app_delegate_blocks[i]) {
65+
g_pending_app_delegate_blocks[i] = nil; // Release the block
66+
}
67+
}
68+
// All pending blocks cleared, reset count.
69+
g_pending_block_count = 0;
70+
}
5471
}
5572

5673
if (g_original_setDelegate_imp) {
@@ -141,17 +158,21 @@ - (BOOL)application:(UIApplication *)application
141158
void ForEachAppDelegateClass(void (^block)(Class)) {
142159
if (g_app_delegate_class) {
143160
NSLog(@"Firebase: ForEachAppDelegateClass executing with stored delegate class: %s.",
144-
class_getName(g_app_delegate_class));
161+
class_getName(g_app_delegate_class));
145162
block(g_app_delegate_class);
146-
// Clear any pending block as we've now executed with a known delegate.
147-
if (g_pending_app_delegate_block) {
148-
g_pending_app_delegate_block = nil; // ARC handles release
149-
}
163+
// If the delegate is already known and we execute immediately,
164+
// any previously pending blocks should have been cleared by Firebase_setDelegate.
165+
// No need to touch g_pending_app_delegate_blocks here as they are for pre-setDelegate calls.
150166
} else {
151-
NSLog(@"Firebase: ForEachAppDelegateClass - delegate class not yet known. Saving block for later execution.");
152-
// If a block is already pending, the new one replaces it. ARC handles the old one.
153-
// Make sure to copy the block to move it to the heap.
154-
g_pending_app_delegate_block = [block copy];
167+
// Delegate class not yet known, try to queue the block.
168+
if (g_pending_block_count < MAX_PENDING_APP_DELEGATE_BLOCKS) {
169+
g_pending_app_delegate_blocks[g_pending_block_count] = [block copy];
170+
g_pending_block_count++;
171+
NSLog(@"Firebase: ForEachAppDelegateClass - delegate class not yet known. Saved block for later execution (pending count: %d).", g_pending_block_count);
172+
} else {
173+
NSLog(@"Firebase Error: ForEachAppDelegateClass - pending block queue is full (max %d). Discarding new block.", MAX_PENDING_APP_DELEGATE_BLOCKS);
174+
// Block is discarded.
175+
}
155176
}
156177
}
157178

0 commit comments

Comments
 (0)