Skip to content

Commit d299673

Browse files
Refactor ForEachAppDelegateClass for iOS into a new function that swizzles [UIApplication setDelegate:] to obtain App Delegate classes. (#1737)
* Refactor ForEachAppDelegateClass for iOS to use swizzling This commit refactors the `ForEachAppDelegateClass` function in `app/src/util_ios.mm`. Instead of scanning all runtime classes to find UIApplicationDelegate implementers, it now relies on method swizzling. The `[UIApplication setDelegate:]` method is swizzled at startup. When `setDelegate:` is called, the class of the actual application delegate is captured and stored globally (static to the .mm file). `ForEachAppDelegateClass` now uses this stored class. If it's called before `setDelegate:` has been invoked, the block passed to `ForEachAppDelegateClass` is queued. This queued block is then executed once the delegate is set via the swizzled `setDelegate:` method. This approach is more efficient and directly targets the actual App Delegate class used by the application. Key changes: - Added `Firebase_setDelegate` C function as the swizzled implementation. - Introduced `UIApplication(FirebaseAppDelegateSwizzling)` category with a `+load` method to perform the swizzling. - Uses `method_setImplementation` for swizzling and stores the original IMP. - Global static variables `g_app_delegate_class`, `g_original_setDelegate_imp`, and `g_pending_app_delegate_block` manage the state within `util_ios.mm`. - Modified `ForEachAppDelegateClass` to use the new mechanism and queue blocks if the delegate is not yet known. * I've tidied up `util_ios.mm` for you. I replaced some logging calls with NSLog for better stability during early app startup. I also removed some unnecessary comments and unused include statements to keep things clean. * 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. * fixup: Cleanup comments and update RunOnAppDelegate call in messaging - I removed extraneous developmental comments from app/src/util_ios.mm for better code clarity. - I updated a call site of firebase::util::RunOnAppDelegate (formerly ForEachAppDelegateClass) in messaging/src/ios/messaging.mm to use the new function name. * I've addressed your review comments. - I updated the documentation for RunOnAppDelegateClasses (formerly RunOnAppDelegate) in app/src/util_ios.h to accurately reflect its new behavior. - I renamed RunOnAppDelegate to RunOnAppDelegateClasses in all relevant locations (declaration, definition, internal logs, and call sites in invites and messaging modules) for clarity. - I removed the specified extraneous code comments from app/src/util_ios.mm and app/src/invites/ios/invites_ios_startup.mm. * fix: Make ClassMethodImplementationCache robust against re-swizzling I modified `ClassMethodImplementationCache::ReplaceOrAddMethod` in app/src/util_ios.mm to prevent re-swizzling a method if it's already swizzled with the target implementation. This is done by checking if the current method IMP is identical to the incoming IMP; if so, the function returns early. This resolves a recursive call issue observed when App Delegate hooks were applied multiple times to the same effective class via different GUL-proxied delegate instances. I also included a final cleanup of specified iterative code comments. * fix: Check superclasses before processing new delegates Modified `Firebase_setDelegate` in `app/src/util_ios.mm` to prevent redundant processing for delegate classes that are subclasses of already seen delegates. - When `setDelegate:` is called with a `newClass`: - It now first iterates through the superclasses of `newClass`. If any superclass is found in the `g_seen_delegate_classes` list, `newClass` is considered handled, and no further processing (adding to seen list or running pending blocks for it) occurs. - If no superclass is seen, it checks if `newClass` itself is already seen. If so, it's skipped. - If `newClass` is genuinely new (neither itself nor any superclass already seen), it's added to `g_seen_delegate_classes`, and all blocks from `g_pending_app_delegate_blocks` are executed for it. - This addresses potential issues with third-party libraries (like GUL) that might set their own delegate subclasses, ensuring our hooks and blocks run appropriately. - Includes cleanup of minor iterative comments. * fix: Restore source code changes and integrate learnings This commit addresses several items after an accidental reset: 1. **Restores Source Code Logic:** * Re-implements the correct logic for `RunOnAppDelegateClasses` (formerly ForEachAppDelegateClass) and the swizzled `Firebase_setDelegate` in `app/src/util_ios.mm`. * `Firebase_setDelegate` now correctly tracks multiple unique delegate classes seen, includes a superclass check to prevent redundant processing for subclasses of already-seen delegates, and executes persistent pending blocks for genuinely new delegate classes. * `RunOnAppDelegateClasses` executes blocks for all currently known unique delegates and queues the block for future new delegate classes. * Ensures `ClassMethodImplementationCache` is in its state prior to the reverted idempotency fix attempt. * All associated constants, global variables, function declarations (in `util_ios.h`), and call sites (in `invites` and `messaging` modules) are correctly restored/updated. * Logging uses `NSLog` and iterative comments have been cleaned. 2. **Integrates Learnings into `Jules.md`:** * Reverts the previous commit that added a task-specific learnings section. * Integrates key insights from this refactoring task directly into the most appropriate existing sections of `Jules.md`, covering robust swizzling, callback lifecycle, naming, logging safety, and agent interaction patterns. This commit aims to bring the branch to the desired functional state with updated documentation. * fix: Add idempotency to swizzling cache and update Jules.md - Re-implemented the idempotency check in ClassMethodImplementationCache::ReplaceOrAddMethod in app/src/util_ios.mm. This prevents re-swizzling if a method already has the target IMP, addressing potential recursion issues. - Updated Jules.md: - Integrated learnings from the recent iOS App Delegate refactoring task into relevant existing sections (covering robust swizzling, callback lifecycles, naming, logging safety, and agent interaction). - Added a document convention note to maintain 80-character line wrapping. - Word-wrapped the entire document to 80 characters for readability. This commit consolidates the fix for the swizzling cache and the comprehensive updates and formatting for Jules.md. * fix: Restore swizzling category and consolidate all recent changes This commit addresses several critical fixes and consolidates all recent updates for the iOS App Delegate handling mechanism: 1. **Restored Swizzling Mechanism:** - Re-added the `UIApplication(FirebaseAppDelegateSwizzling)` category and its `+load` method to `app/src/util_ios.mm`. This was inadvertently lost during a previous operation and is essential for swizzling `[UIApplication setDelegate:]` with `Firebase_setDelegate`. 2. **Core Logic (already in working tree, confirmed):** - `Firebase_setDelegate` correctly tracks multiple unique delegate classes, includes a superclass check, and executes persistent pending blocks for genuinely new delegate classes. - `RunOnAppDelegateClasses` executes blocks for all currently known delegates and queues blocks for future new delegates. - `ClassMethodImplementationCache::ReplaceOrAddMethod` includes an idempotency check to prevent re-swizzling if a method already has the target IMP. 3. **Documentation (`Jules.md`):** - Learnings from this refactoring are integrated into relevant existing sections. - The document is formatted with 80-character line wrapping, and a note regarding this convention is included. This commit aims to bring the `refactor-forEachAppDelegateClass-ios` branch to its fully intended functional state, including all logic fixes, restorations, and documentation updates. * iOS: Allow specifying your AppDelegate class's name in the Info.plist file. (#1741) * Feature: Allow specifying AppDelegate via Info.plist for RunOnAppDelegateClasses 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. - `[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, especially in scenarios where swizzling might be undesirable or needs to be more targeted. Detailed logging has been added to trace the behavior in both modes. A manual testing plan has been outlined to cover various scenarios. * 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. * Refactor: Improve comments and logging for AppDelegate Info.plist feature This commit cleans up comments and refines logging messages within the `+load` method in `FirebaseAppDelegateSwizzling` category for clarity and accuracy related to the recently added feature for specifying the AppDelegate via Info.plist. - Clarified comments explaining the Info.plist handling path, including the setup of the specified delegate and the execution of pending blocks. - Ensured comments accurately reflect that pending blocks are not cleared from the queue after execution in `+load` when in Info.plist mode. - Minor wording improvements to log messages for better diagnostics. - Removed redundant or outdated comments from previous iterations. No functional code changes are included in this commit. * Docs: Simplify AppDelegate Info.plist option in README Further refines the documentation for the `FirebaseAppDelegateClassName` Info.plist key feature on iOS. - The explanation in `release_build_files/readme.md` under "Specifying Your AppDelegate Class Directly (iOS)" has been made more concise and user-focused, removing internal implementation details. - The corresponding release note for version 12.9.0 has also been simplified to match this approach. This change aims to make the documentation easier for developers to understand by focusing on the action and benefit rather than Firebase internal mechanisms. * Docs: Use generic 'Upcoming Release' title in README Changes the heading for the newest release notes entry from '### 12.9.0 (Upcoming)' to '### Upcoming Release' as the specific version number is not yet known. * Update logging to not be verbose unless debug logs are on. Also clean up NSLog messages elsewhere. * Update log message. * Fix build error. * Format code. --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> * Fix build error. * Finish renaming Jules.md to AGENTS.md. --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent 5d44914 commit d299673

File tree

6 files changed

+383
-96
lines changed

6 files changed

+383
-96
lines changed

AGENTS.md

Lines changed: 75 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Introduction
22

3+
> **Note on Document Formatting:** This document (`AGENTS.md`) should be
4+
> maintained with lines word-wrapped to a maximum of 80 characters to ensure
5+
> readability across various editors and terminals.
6+
37
This document provides context and guidance for AI agents (like Jules) when
48
making changes to the Firebase C++ SDK repository. It covers essential
59
information about the repository's structure, setup, testing procedures, API
@@ -34,8 +38,8 @@ instructions for your specific platform.
3438
* **Android SDK & NDK**: Required for building Android libraries. `sdkmanager`
3539
can be used for installation. CMake for Android (version 3.10.2
3640
recommended) is also needed.
37-
* **(Windows Only) Strings**: From Microsoft Sysinternals, required for Android
38-
builds on Windows.
41+
* **(Windows Only) Strings**: From Microsoft Sysinternals, required for
42+
Android builds on Windows.
3943
* **Cocoapods**: Required for building iOS or tvOS libraries.
4044

4145
## Building the SDK
@@ -74,9 +78,10 @@ generated in each library's build directory (e.g.,
7478

7579
### Desktop Platform Setup Details
7680

77-
When setting up for desktop, if you are using an iOS `GoogleService-Info.plist`
78-
file, convert it to the required `google-services-desktop.json` using the
79-
script: `python generate_xml_from_google_services_json.py --plist -i GoogleService-Info.plist`
81+
When setting up for desktop, if you are using an iOS
82+
`GoogleService-Info.plist` file, convert it to the required
83+
`google-services-desktop.json` using the script:
84+
`python generate_xml_from_google_services_json.py --plist -i GoogleService-Info.plist`
8085
(run this from the script's directory, ensuring the plist file is accessible).
8186

8287
The desktop SDK searches for configuration files in the current working
@@ -175,8 +180,9 @@ Database).
175180
parameters if not relying on a `google-services.json` or
176181
`GoogleService-Info.plist` file.
177182
2. **Service Instances**: Once `firebase::App` is initialized, you generally
178-
obtain instances of specific Firebase services using a static `GetInstance()`
179-
method on the service's class, passing the `firebase::App` object.
183+
obtain instances of specific Firebase services using a static
184+
`GetInstance()` method on the service's class, passing the `firebase::App`
185+
object.
180186
* Examples for services like Auth, Database, Storage, Firestore:
181187
* `firebase::auth::Auth* auth = firebase::auth::Auth::GetAuth(app, &init_result);`
182188
* `firebase::database::Database* database = firebase::database::Database::GetInstance(app, &init_result);`
@@ -192,8 +198,8 @@ Database).
192198
called as global functions within the `firebase::analytics` namespace,
193199
rather than on an instance object obtained via `GetInstance()`.
194200
Refer to the specific product's header file for its exact
195-
initialization mechanism if it deviates from the common `GetInstance(app, ...)`
196-
pattern.
201+
initialization mechanism if it deviates from the common
202+
`GetInstance(app, ...)` pattern.
197203

198204
### Asynchronous Operations: `firebase::Future<T>`
199205

@@ -205,8 +211,8 @@ where `T` is the type of the expected result.
205211
`kFutureStatusInvalid`.
206212
* **Getting Results**: Once `future.status() == kFutureStatusComplete`:
207213
* Check for errors: `future.error()`. A value of `0` (e.g.,
208-
`firebase::auth::kAuthErrorNone`, `firebase::database::kErrorNone`)
209-
usually indicates success.
214+
`firebase::auth::kAuthErrorNone`,
215+
`firebase::database::kErrorNone`) usually indicates success.
210216
* Get the error message: `future.error_message()`.
211217
* Get the result: `future.result()`. This returns a pointer to the result
212218
object of type `T`. The result is only valid if `future.error()`
@@ -218,8 +224,8 @@ where `T` is the type of the expected result.
218224

219225
### Core Classes and Operations (Examples from Auth and Database)
220226

221-
While each Firebase product has its own specific classes, the following examples
222-
illustrate common API patterns:
227+
While each Firebase product has its own specific classes, the following
228+
examples illustrate common API patterns:
223229

224230
* **`firebase::auth::Auth`**: The main entry point for Firebase
225231
Authentication.
@@ -305,6 +311,12 @@ API documentation.
305311
as mentioned in `CONTRIBUTING.md`.
306312
* **Formatting**: Use `python3 scripts/format_code.py -git_diff -verbose` to
307313
format your code before committing.
314+
* **Naming Precision for Dynamic Systems**: Function names should precisely
315+
reflect their behavior, especially in systems with dynamic or asynchronous
316+
interactions. For example, a function that processes a list of items should
317+
be named differently from one that operates on a single, specific item
318+
captured asynchronously. Regularly re-evaluate function names as
319+
requirements evolve to maintain clarity.
308320

309321
## Comments
310322

@@ -328,8 +340,9 @@ API documentation.
328340
* **Check `Future` status and errors**: Always check `future.status()` and
329341
`future.error()` before attempting to use `future.result()`.
330342
* A common success code is `0` (e.g.,
331-
`firebase::auth::kAuthErrorNone`, `firebase::database::kErrorNone`).
332-
Other specific error codes are defined per module (e.g.,
343+
`firebase::auth::kAuthErrorNone`,
344+
`firebase::database::kErrorNone`). Other specific error codes are
345+
defined per module (e.g.,
333346
`firebase::auth::kAuthErrorUserNotFound`).
334347
* **Callback error parameters**: When using listeners or other callbacks,
335348
always check the provided error code and message before processing the
@@ -362,6 +375,13 @@ API documentation.
362375
otherwise ensuring the `Future` completes its course, particularly for
363376
operations with side effects or those that allocate significant backend
364377
resources.
378+
* **Lifecycle of Queued Callbacks/Blocks**: If blocks or callbacks are queued
379+
to be run upon an asynchronous event (e.g., an App Delegate class being set
380+
or a Future completing), clearly define and document their lifecycle.
381+
Determine if they are one-shot (cleared after first execution) or
382+
persistent (intended to run for multiple or future events). This impacts
383+
how associated data and the blocks themselves are stored and cleared,
384+
preventing memory leaks or unexpected multiple executions.
365385

366386
## Immutability
367387

@@ -397,6 +417,29 @@ API documentation.
397417
integration, it can occasionally be a factor to consider when debugging app
398418
delegate behavior or integrating with other libraries that also perform
399419
swizzling.
420+
When implementing or interacting with swizzling, especially for App Delegate
421+
methods like `[UIApplication setDelegate:]`:
422+
* Be highly aware that `setDelegate:` can be called multiple times
423+
with different delegate class instances, including proxy classes
424+
from other libraries (e.g., GUL - Google Utilities). Swizzling
425+
logic must be robust against being invoked multiple times for the
426+
same effective method on the same class or on classes in a
427+
hierarchy. An idempotency check (i.e., if the method's current IMP
428+
is already the target swizzled IMP, do nothing more for that
429+
specific swizzle attempt) in any swizzling utility can prevent
430+
issues like recursion.
431+
* When tracking unique App Delegate classes (e.g., for applying hooks
432+
or callbacks via swizzling), consider the class hierarchy. If a
433+
superclass has already been processed, processing a subclass for
434+
the same inherited methods might be redundant or problematic. A
435+
strategy to check if a newly set delegate is a subclass of an
436+
already processed delegate can prevent such issues.
437+
* For code that runs very early in the application lifecycle on
438+
iOS/macOS (e.g., `+load` methods, static initializers involved in
439+
swizzling), prefer using `NSLog` directly over custom logging
440+
frameworks if there's any uncertainty about whether the custom
441+
framework is fully initialized, to avoid crashes during logging
442+
itself.
400443

401444
## Class and File Structure
402445

@@ -462,9 +505,9 @@ management:
462505
module, but the fundamental responsibility for creation and deletion in
463506
typical scenarios lies with the Pimpl class itself.
464507

465-
It's crucial to correctly implement all these aspects (constructors, destructor,
466-
copy/move operators) when dealing with raw pointer Pimpls to prevent memory
467-
leaks, dangling pointers, or double deletions.
508+
It's crucial to correctly implement all these aspects (constructors,
509+
destructor, copy/move operators) when dealing with raw pointer Pimpls to
510+
prevent memory leaks, dangling pointers, or double deletions.
468511

469512
## Namespace Usage
470513

@@ -531,7 +574,7 @@ leaks, dangling pointers, or double deletions.
531574
This document is a living guide. As the Firebase C++ SDK evolves, new patterns
532575
may emerge, or existing practices might change. If you introduce a new common
533576
pattern, significantly alter a build process, or establish a new best practice
534-
during your work, please take a moment to update this `Jules.md` file
577+
during your work, please take a moment to update this `AGENTS.md` file
535578
accordingly.
536579

537580
Keeping this document current will greatly benefit future AI agents and human
@@ -541,38 +584,43 @@ developers working on this repository.
541584

542585
## Recommended General Prompt Instruction
543586

544-
When working on this task, please consistently refer to the `Jules.md` guide
587+
When working on this task, please consistently refer to the `AGENTS.md` guide
545588
for all repository-specific conventions, including setup procedures, coding
546589
style, common architectural patterns, and API usage. Pay close attention to the
547590
testing strategies outlined, ensuring your implementation includes
548591
comprehensive integration tests with detailed test cases in your plan. Implement
549592
robust error handling for any new or modified public API methods, following the
550593
patterns for `firebase::Future` error reporting. If the feature involves
551594
platform-specific code, ensure the public API remains consistent across all
552-
platforms, as discussed in `Jules.md`. Write clear, maintainable code,
595+
platforms, as discussed in `AGENTS.md`. Write clear, maintainable code,
553596
adhering to the commenting guidelines, and if you need to add new third-party
554597
dependencies, document the rationale and update build configurations according
555598
to the established practices. Ensure your changes align with the overall best
556-
practices detailed in `Jules.md`.
599+
practices detailed in `AGENTS.md`.
557600

558601
## Key Directives for Jules AI
559602

560-
* **Prioritize `Jules.md`**: This document (`Jules.md`) contains
603+
* **Prioritize `AGENTS.md`**: This document (`AGENTS.md`) contains
561604
repository-specific guidelines. Prioritize this information when making
562605
decisions about coding style, testing procedures, architectural patterns,
563606
and API usage.
564607
* **Adherence to Patterns**: Strive to follow the common patterns and best
565608
practices outlined here. This ensures consistency across the codebase.
566-
* **Clarification for Deviations**: If the existing patterns in `Jules.md` do
609+
* **Clarification for Deviations**: If the existing patterns in `AGENTS.md` do
567610
not seem suitable for a specific task, or if a deviation is necessary,
568611
please explicitly state this in your plan or request clarification before
569612
proceeding with implementation.
570-
* **Updating `Jules.md`**: If you introduce a new, broadly applicable
613+
* **Updating `AGENTS.md`**: If you introduce a new, broadly applicable
571614
pattern, or if a significant change to the build process or best practices
572615
occurs as part of your task, please include a step in your plan to update
573-
`Jules.md` to reflect these changes.
616+
`AGENTS.md` to reflect these changes.
574617
* **Testing**: Remember that integration tests are the primary method of
575618
testing. Ensure new features are accompanied by corresponding integration
576-
tests as described in the 'Testing' section of `Jules.md`.
619+
tests as described in the 'Testing' section of `AGENTS.md`.
577620
* **Commit Messages**: Follow standard commit message guidelines. A brief
578621
summary line, followed by a more detailed explanation if necessary.
622+
* **Tool Limitations & Path Specificity**: If codebase search tools (like
623+
`grep` or recursive `ls`) are limited or unavailable, and initial attempts
624+
to locate files/modules based on common directory structures are
625+
unsuccessful, explicitly ask for more specific paths rather than assuming a
626+
module doesn't exist or contains no relevant code.

app/src/invites/ios/invites_ios_startup.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ @implementation UIApplication (FIRFBI)
286286
+ (void)load {
287287
// C++ constructors may not be called yet so call NSLog rather than LogDebug.
288288
NSLog(@"Loading UIApplication category for Firebase App");
289-
::firebase::util::ForEachAppDelegateClass(^(Class clazz) {
289+
::firebase::util::RunOnAppDelegateClasses(^(Class clazz) {
290290
::firebase::invites::HookAppDelegateMethods(clazz);
291291
});
292292
}

app/src/util_ios.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,12 @@ typedef BOOL (
185185
id self, SEL selector_value, UIApplication *application,
186186
NSUserActivity *user_activity, void (^restoration_handler)(NSArray *));
187187

188-
// Call the given block once for every Objective-C class that exists that
189-
// implements the UIApplicationDelegate protocol (except for those in a
190-
// blacklist we keep).
191-
void ForEachAppDelegateClass(void (^block)(Class));
188+
// Calls the given block for each unique Objective-C class that has been
189+
// previously passed to [UIApplication setDelegate:]. The block is executed
190+
// immediately for all currently known unique delegate classes.
191+
// Additionally, the block is queued to be executed if any new, unique
192+
// Objective-C class is passed to [UIApplication setDelegate:] in the future.
193+
void RunOnAppDelegateClasses(void (^block)(Class));
192194

193195
// Convert a string array into an NSMutableArray.
194196
NSMutableArray *StringVectorToNSMutableArray(

0 commit comments

Comments
 (0)