Paul Beusterien 7c53489e03 Travis to Xcode 10.1 and clang-format to 8.0.0 (tags/google/stable/2018-08-24) (#2222) 7 gadi atpakaļ
..
Internal a7117ea786 Fix nullability analysis error in GULAppDelegateSwizzler (#1749) 7 gadi atpakaļ
Private b88f95c44f Reformat using 'clang-format version 7.0.0 (tags/google/stable/2018-04-24)' (#1681) 7 gadi atpakaļ
GULAppDelegateSwizzler.m 7c53489e03 Travis to Xcode 10.1 and clang-format to 8.0.0 (tags/google/stable/2018-08-24) (#2222) 7 gadi atpakaļ
README.md 2c5bafb7e5 Add documentation on how to use the app delegate swizzler. (#1750) 7 gadi atpakaļ

README.md

App Delegate Swizzler

Overview

The App Delegate Swizzler swizzles certain methods on the AppDelegate and allows interested parties (for eg. other SDKs like Firebase Analytics) to register listeners when certain App Delegate methods are called.

The App Delegate Swizzler uses isa swizzling to create a dynamic subclass of the app delegate class and add methods to it that have the logic to add multiple "interceptors".

Adding interceptors to the following methods is currently supported by the App Delegate Swizzler.

  • - (BOOL)application:openURL:options: Reference

Note: This method is added only if the original app delegate implements it. This prevents a bug where if an app only implements application:openURL:sourceApplication:annotation: and if we add the options method, iOS sees that the options method exists and so does not call the sourceApplication method, which causes the app developer's logic in sourceApplication to not be called.

  • - (BOOL)application:openURL:sourceApplication:annotation: Reference

  • - (void)application:handleEventsForBackgroundURLSession:completionHandler: Reference

  • - (BOOL)application:continueUserActivity:restorationHandler: Reference

We are looking into adding support for more methods as we need them.

Adopting the swizzler

To start using the app delegate swizzler to intercept app delegate methods do the following:

The following assumes that you are an SDK that ships using Cocoapods and need to react to one of the app delegate methods listed above.

  1. Add a dependency to the app delegate swizzler - GoogleUtilities/AppDelegateSwizzler:~> 5.2. We follow Semantic Versioning.

  2. Create an interceptor class that implements the UIApplicationDelegate and implements the methods you want to intercept. For eg.

MYAppDelegateInterceptor.h


#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

/// An instance of this class is meant to be registered as an AppDelegate interceptor, and
/// implements the logic that my SDK needs to perform when certain app delegate methods are invoked.
@interface MYAppDelegateInterceptor : NSObject <UIApplicationDelegate>

/// Returns the MYAppDelegateInterceptor singleton.
/// Always register just this singleton as the app delegate interceptor. This instance is
/// retained. The App Delegate Swizzler only retains weak references and so this is needed.
+ (instancetype)sharedInstance;

@end

NS_ASSUME_NONNULL_END

MYAppDelegateInterceptor.m

#import "MYAppDelegateInterceptor.h"

@implementation MYAppDelegateInterceptor

+ (instancetype)sharedInstance {
  static dispatch_once_t once;
  static MYAppDelegateInterceptor *sharedInstance;
  dispatch_once(&once, ^{
    sharedInstance = [[MYAppDelegateInterceptor alloc] init];
  });
  return sharedInstance;
}

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)URL
            options:(NSDictionary<NSString *, id> *)options {

  [MYInterestingClass doSomething];

  // Results of this are ORed and NO doesn't affect other delegate interceptors' result.
  return NO;
}

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)URL
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation {

    [MYInterestingClass doSomething];

  // Results of this are ORed and NO doesn't affect other delegate interceptors' result.
  return NO;
}

#pragma mark - Network overridden handler methods

- (void)application:(UIApplication *)application
    handleEventsForBackgroundURLSession:(NSString *)identifier
                      completionHandler:(void (^)(void))completionHandler {

  // Note: Interceptors are not responsible for (and should not) call the completion handler.
  [MYInterestingClass doSomething];
}

#pragma mark - User Activities overridden handler methods

- (BOOL)application:(UIApplication *)application
    continueUserActivity:(NSUserActivity *)userActivity
      restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {

  [MYInterestingClass doSomething];

  // Results of this are ORed and NO doesn't affect other delegate interceptors' result.
  return NO;
}

@end
  1. Register your interceptor when it makes sense to do so.

For eg.


// MYInterestingClass.m

#import <GoogleUtilities/GULAppDelegateSwizzler.h>

...

- (void)someInterestingMethod {
    ...

    // Calling this ensures that the app delegate is proxied (has no effect if some other SDK has
    // already done it).
    [GULAppDelegateSwizzler proxyOriginalDelegate];

    MYAppDelegateInterceptor *interceptor = [MYAppDelegateInterceptor sharedInstance];
    [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor];
}

Disabling App Delegate Swizzling by App Developers

Sometimes app developers that consume our SDKs prefer that we do not swizzle the app delegate. We've added support for developers to disable any sort of app delegate swizzling that we may do, and this is achieved by adding the Plist flag GoogleUtilitiesAppDelegateProxyEnabled to NO (Boolean). If this is set, even if you call [GULAppDelegateSwizzler proxyOriginalDelegate], it won't have any effect.