Skip to content

Commit faae161

Browse files
Feature: Allow specifying AppDelegate via Info.plist for RunOnAppDelegateClasses (Refined)
Currently, `firebase::util::RunOnAppDelegateClasses` on iOS automatically swizzles `[UIApplication setDelegate:]` to capture and act on any class set as the application delegate. This change introduces an optional feature where developers can specify their app's main AppDelegate class name directly in the `Info.plist` file using the key `FirebaseAppDelegateClassName`. If this key is present and provides a valid class name: - `RunOnAppDelegateClasses` will only execute blocks for this specified class. - Pending blocks are processed once for this target. - New blocks execute immediately on this target and are not queued for others. - `[UIApplication setDelegate:]` will NOT be swizzled by Firebase. If the key is not present, is invalid, or the specified class is not found, Firebase will fall back to the original behavior of swizzling `[UIApplication setDelegate:]`. This provides developers more control over Firebase's interaction with the AppDelegate. The implementation of `RunOnAppDelegateClasses` has been refined to support this new mode more simply while ensuring correct block execution and pending queue management. Detailed logging has been added. A manual testing plan is provided.
1 parent cfed377 commit faae161

File tree

1 file changed

+54
-60
lines changed

1 file changed

+54
-60
lines changed

app/src/util_ios.mm

Lines changed: 54 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -229,76 +229,70 @@ + (void)load {
229229
namespace util {
230230

231231
void RunOnAppDelegateClasses(void (^block)(Class)) {
232-
if (g_firebase_specific_delegate_mode) {
233-
// Specific Delegate Mode
234-
if (g_firebase_target_app_delegate_class) {
235-
// Check if the target delegate is among the "seen" classes (it should be g_seen_delegate_classes[0])
236-
// This also implies g_seen_delegate_classes_count == 1 due to how +load sets it up.
237-
if (g_seen_delegate_classes_count == 1 && g_seen_delegate_classes[0] == g_firebase_target_app_delegate_class) {
238-
NSLog(@"Firebase: RunOnAppDelegateClasses (Specific Mode) - Applying block for target delegate: %@", NSStringFromClass(g_firebase_target_app_delegate_class));
239-
block(g_firebase_target_app_delegate_class);
240-
241-
// For pending blocks: these should also only run for the target delegate.
242-
// If RunOnAppDelegateClasses is called multiple times, pending blocks should only execute once for the target.
243-
static dispatch_once_t once_token_specific_delegate_pending_blocks;
244-
dispatch_once(&once_token_specific_delegate_pending_blocks, ^{
245-
if (g_pending_block_count > 0) {
246-
NSLog(@"Firebase: RunOnAppDelegateClasses (Specific Mode) - Executing %d pending block(s) for target delegate: %@", g_pending_block_count, NSStringFromClass(g_firebase_target_app_delegate_class));
247-
for (int i = 0; i < g_pending_block_count; i++) {
248-
if (g_pending_app_delegate_blocks[i]) {
249-
g_pending_app_delegate_blocks[i](g_firebase_target_app_delegate_class);
250-
g_pending_app_delegate_blocks[i] = nil; // Clear after execution
251-
}
252-
}
253-
g_pending_block_count = 0; // All pending blocks consumed for the specific target
254-
}
255-
});
256-
// Do NOT add the current 'block' to g_pending_app_delegate_blocks in this mode after the initial processing of pending blocks.
257-
// Each new call to RunOnAppDelegateClasses with a 'block' will execute it immediately on the target.
258-
// If pending blocks haven't been processed yet (before dispatch_once runs), new blocks might need queuing.
259-
// However, the dispatch_once ensures pending blocks are processed once. A new block passed to this function
260-
// will execute above. If it needs to be "pending" for the specific delegate, it implies the specific delegate
261-
// itself isn't "active" yet, which contradicts this mode.
262-
// The logic is: if specific delegate is known, all blocks run on it. Pending is for when it's not yet known.
263-
} else {
264-
// This state implies g_firebase_target_app_delegate_class is set, but it's not yet in g_seen_delegate_classes,
265-
// or g_seen_delegate_classes is not correctly set to [target, nil, ...] count 1.
266-
// This might happen if RunOnAppDelegateClasses is called *very* early, even before +load fully configures g_seen_delegate_classes for specific mode.
267-
// In this scenario, we should queue the block.
268-
NSLog(@"Firebase: RunOnAppDelegateClasses (Specific Mode) - Target delegate %@ not yet fully processed or g_seen_delegate_classes mismatch. Queuing block.", NSStringFromClass(g_firebase_target_app_delegate_class));
269-
if (g_pending_block_count < MAX_PENDING_APP_DELEGATE_BLOCKS) {
270-
g_pending_app_delegate_blocks[g_pending_block_count] = [block copy];
271-
g_pending_block_count++;
272-
NSLog(@"Firebase: RunOnAppDelegateClasses (Specific Mode) - added block to pending list (total pending: %d).", g_pending_block_count);
273-
} else {
274-
NSLog(@"Firebase Error: RunOnAppDelegateClasses (Specific Mode) - pending block queue full. Discarding block.");
275-
}
232+
// Execute the block for any already seen/specified delegate(s).
233+
// In specific mode, g_seen_delegate_classes_count is 1, and g_seen_delegate_classes[0] is the target.
234+
// In swizzle mode, this iterates through all delegates captured by the swizzled setDelegate:.
235+
if (g_seen_delegate_classes_count > 0) {
236+
NSLog(@"Firebase: RunOnAppDelegateClasses - Executing block for %d seen/specified delegate(s). Mode: %@",
237+
g_seen_delegate_classes_count, g_firebase_specific_delegate_mode ? @"Specific" : @"Swizzle");
238+
for (int i = 0; i < g_seen_delegate_classes_count; i++) {
239+
if (g_seen_delegate_classes[i]) { // Safety check
240+
block(g_seen_delegate_classes[i]);
276241
}
277-
} else {
278-
// Should not happen if +load correctly sets g_firebase_target_app_delegate_class when g_firebase_specific_delegate_mode is true.
279-
NSLog(@"Firebase Error: RunOnAppDelegateClasses (Specific Mode) - Target delegate class is nil. Block not executed or queued.");
280242
}
281243
} else {
282-
// Original Swizzling Mode (existing logic)
283-
if (g_seen_delegate_classes_count > 0) {
284-
NSLog(@"Firebase: RunOnAppDelegateClasses (Swizzle Mode) executing block for %d already seen delegate class(es).",
285-
g_seen_delegate_classes_count);
286-
for (int i = 0; i < g_seen_delegate_classes_count; i++) {
287-
if (g_seen_delegate_classes[i]) { // Safety check
288-
block(g_seen_delegate_classes[i]);
244+
// This case should primarily occur in swizzle mode if no delegate has been set yet.
245+
// In specific mode, +load should have set g_seen_delegate_classes_count to 1.
246+
// If it's 0 in specific mode, it implies an issue or very early call, so queuing is reasonable.
247+
NSLog(@"Firebase: RunOnAppDelegateClasses - No delegate classes seen yet. Mode: %@. Block will be queued.",
248+
g_firebase_specific_delegate_mode ? @"Specific" : @"Swizzle");
249+
}
250+
251+
// Handle pending blocks and queuing of the current block based on mode.
252+
if (g_firebase_specific_delegate_mode) {
253+
// Specific Delegate Mode:
254+
// Process any previously pending blocks for the target delegate (once).
255+
if (g_firebase_target_app_delegate_class) { // Ensure target is known
256+
static dispatch_once_t once_token_specific_delegate_pending_blocks;
257+
dispatch_once(&once_token_specific_delegate_pending_blocks, ^{
258+
if (g_pending_block_count > 0) {
259+
NSLog(@"Firebase: RunOnAppDelegateClasses (Specific Mode) - Executing %d PENDING block(s) for target delegate: %@",
260+
g_pending_block_count, NSStringFromClass(g_firebase_target_app_delegate_class));
261+
for (int i = 0; i < g_pending_block_count; i++) {
262+
if (g_pending_app_delegate_blocks[i]) {
263+
g_pending_app_delegate_blocks[i](g_firebase_target_app_delegate_class);
264+
g_pending_app_delegate_blocks[i] = nil; // Clear after execution
265+
}
266+
}
267+
g_pending_block_count = 0; // All pending blocks consumed for the specific target
268+
}
269+
});
270+
}
271+
// Do NOT queue the current 'block' for "future delegates" in specific mode.
272+
// If g_seen_delegate_classes_count was 0 above (e.g. called before +load fully set up specific mode),
273+
// the block needs to be queued to run on the specific delegate once it's identified.
274+
if (g_seen_delegate_classes_count == 0 && g_firebase_target_app_delegate_class) { // Target known but not yet in g_seen_delegate_classes
275+
if (g_pending_block_count < MAX_PENDING_APP_DELEGATE_BLOCKS) {
276+
g_pending_app_delegate_blocks[g_pending_block_count] = [block copy];
277+
g_pending_block_count++;
278+
NSLog(@"Firebase: RunOnAppDelegateClasses (Specific Mode) - Target delegate %@ not in seen list yet. Current block queued (total pending: %d).",
279+
NSStringFromClass(g_firebase_target_app_delegate_class), g_pending_block_count);
280+
} else {
281+
NSLog(@"Firebase Error: RunOnAppDelegateClasses (Specific Mode) - Pending block queue full. Cannot queue current block for target %@.",
282+
NSStringFromClass(g_firebase_target_app_delegate_class));
289283
}
290-
}
291284
} else {
292-
NSLog(@"Firebase: RunOnAppDelegateClasses (Swizzle Mode) - no delegate classes seen yet. Block will be queued for future delegates.");
285+
NSLog(@"Firebase: RunOnAppDelegateClasses (Specific Mode) - Block already executed for target or no target. Not adding to pending queue.");
293286
}
294-
295-
// Always try to queue the block for any future new delegate classes in swizzle mode.
287+
} else {
288+
// Original Swizzling Mode:
289+
// Queue the current block if no delegates seen yet, or always for future new delegates.
296290
if (g_pending_block_count < MAX_PENDING_APP_DELEGATE_BLOCKS) {
297291
g_pending_app_delegate_blocks[g_pending_block_count] = [block copy];
298292
g_pending_block_count++;
299-
NSLog(@"Firebase: RunOnAppDelegateClasses (Swizzle Mode) - added block to pending list (total pending: %d). This block will run on future new delegate classes.", g_pending_block_count);
293+
NSLog(@"Firebase: RunOnAppDelegateClasses (Swizzle Mode) - Added block to pending list (total pending: %d). This block will run on future new delegate classes.", g_pending_block_count);
300294
} else {
301-
NSLog(@"Firebase Error: RunOnAppDelegateClasses (Swizzle Mode) - pending block queue is full (max %d). Cannot add new block for future execution. Discarding block.", MAX_PENDING_APP_DELEGATE_BLOCKS);
295+
NSLog(@"Firebase Error: RunOnAppDelegateClasses (Swizzle Mode) - Pending block queue is full (max %d). Cannot add new block for future execution. Discarding block.", MAX_PENDING_APP_DELEGATE_BLOCKS);
302296
}
303297
}
304298
}

0 commit comments

Comments
 (0)