Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// Copyright 2018 Google LLC
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
 
15
#import <TargetConditionals.h>
16
 
17
#import "GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h"
18
#import "GoogleUtilities/AppDelegateSwizzler/Public/GoogleUtilities/GULAppDelegateSwizzler.h"
19
#import "GoogleUtilities/Common/GULLoggerCodes.h"
20
#import "GoogleUtilities/Environment/Public/GoogleUtilities/GULAppEnvironmentUtil.h"
21
#import "GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h"
22
#import "GoogleUtilities/Network/Public/GoogleUtilities/GULMutableDictionary.h"
23
 
24
#import <dispatch/group.h>
25
#import <objc/runtime.h>
26
 
27
// Implementations need to be typed before calling the implementation directly to cast the
28
// arguments and the return types correctly. Otherwise, it will crash the app.
29
typedef BOOL (*GULRealOpenURLSourceApplicationAnnotationIMP)(
30
    id, SEL, GULApplication *, NSURL *, NSString *, id);
31
 
32
typedef BOOL (*GULRealOpenURLOptionsIMP)(
33
    id, SEL, GULApplication *, NSURL *, NSDictionary<NSString *, id> *);
34
 
35
#pragma clang diagnostic push
36
#pragma clang diagnostic ignored "-Wstrict-prototypes"
37
typedef void (*GULRealHandleEventsForBackgroundURLSessionIMP)(
38
    id, SEL, GULApplication *, NSString *, void (^)());
39
#pragma clang diagnostic pop
40
 
41
typedef BOOL (*GULRealContinueUserActivityIMP)(
42
    id, SEL, GULApplication *, NSUserActivity *, void (^)(NSArray *restorableObjects));
43
 
44
typedef void (*GULRealDidRegisterForRemoteNotificationsIMP)(id, SEL, GULApplication *, NSData *);
45
 
46
typedef void (*GULRealDidFailToRegisterForRemoteNotificationsIMP)(id,
47
                                                                  SEL,
48
                                                                  GULApplication *,
49
                                                                  NSError *);
50
 
51
typedef void (*GULRealDidReceiveRemoteNotificationIMP)(id, SEL, GULApplication *, NSDictionary *);
52
 
53
#if !TARGET_OS_WATCH && !TARGET_OS_OSX
54
typedef void (*GULRealDidReceiveRemoteNotificationWithCompletionIMP)(
55
    id, SEL, GULApplication *, NSDictionary *, void (^)(UIBackgroundFetchResult));
56
#endif  // !TARGET_OS_WATCH && !TARGET_OS_OSX
57
 
58
typedef void (^GULAppDelegateInterceptorCallback)(id<GULApplicationDelegate>);
59
 
60
// The strings below are the keys for associated objects.
61
static char const *const kGULRealIMPBySelectorKey = "GUL_realIMPBySelector";
62
static char const *const kGULRealClassKey = "GUL_realClass";
63
 
64
static NSString *const kGULAppDelegateKeyPath = @"delegate";
65
 
66
static GULLoggerService kGULLoggerSwizzler = @"[GoogleUtilities/AppDelegateSwizzler]";
67
 
68
// Since Firebase SDKs also use this for app delegate proxying, in order to not be a breaking change
69
// we disable App Delegate proxying when either of these two flags are set to NO.
70
 
71
/** Plist key that allows Firebase developers to disable App and Scene Delegate Proxying. */
72
static NSString *const kGULFirebaseAppDelegateProxyEnabledPlistKey =
73
    @"FirebaseAppDelegateProxyEnabled";
74
 
75
/** Plist key that allows developers not using Firebase to disable App and Scene Delegate Proxying.
76
 */
77
static NSString *const kGULGoogleUtilitiesAppDelegateProxyEnabledPlistKey =
78
    @"GoogleUtilitiesAppDelegateProxyEnabled";
79
 
80
/** The prefix of the App Delegate. */
81
static NSString *const kGULAppDelegatePrefix = @"GUL_";
82
 
83
/** The original instance of App Delegate. */
84
static id<GULApplicationDelegate> gOriginalAppDelegate;
85
 
86
/** The original App Delegate class */
87
static Class gOriginalAppDelegateClass;
88
 
89
/** The subclass of the original App Delegate. */
90
static Class gAppDelegateSubclass;
91
 
92
/** Remote notification methods selectors
93
 *
94
 *  We have to opt out of referencing APNS related App Delegate methods directly to prevent
95
 *  an Apple review warning email about missing Push Notification Entitlement
96
 *  (like here: https://github.com/firebase/firebase-ios-sdk/issues/2807). From our experience, the
97
 *  warning is triggered when any of the symbols is present in the application sent to review, even
98
 *  if the code is never executed. Because GULAppDelegateSwizzler may be used by applications that
99
 *  are not using APNS we have to refer to the methods indirectly using selector constructed from
100
 *  string.
101
 *
102
 *  NOTE: None of the methods is proxied unless it is explicitly requested by calling the method
103
 *  +[GULAppDelegateSwizzler proxyOriginalDelegateIncludingAPNSMethods]
104
 */
105
static NSString *const kGULDidRegisterForRemoteNotificationsSEL =
106
    @"application:didRegisterForRemoteNotificationsWithDeviceToken:";
107
static NSString *const kGULDidFailToRegisterForRemoteNotificationsSEL =
108
    @"application:didFailToRegisterForRemoteNotificationsWithError:";
109
static NSString *const kGULDidReceiveRemoteNotificationSEL =
110
    @"application:didReceiveRemoteNotification:";
111
static NSString *const kGULDidReceiveRemoteNotificationWithCompletionSEL =
112
    @"application:didReceiveRemoteNotification:fetchCompletionHandler:";
113
 
114
/**
115
 * This class is necessary to store the delegates in an NSArray without retaining them.
116
 * [NSValue valueWithNonRetainedObject] also provides this functionality, but does not provide a
117
 * zeroing pointer. This will cause EXC_BAD_ACCESS when trying to access the object after it is
118
 * dealloced. Instead, this container stores a weak, zeroing reference to the object, which
119
 * automatically is set to nil by the runtime when the object is dealloced.
120
 */
121
@interface GULZeroingWeakContainer : NSObject
122
 
123
/** Stores a weak object. */
124
@property(nonatomic, weak) id object;
125
 
126
@end
127
 
128
@implementation GULZeroingWeakContainer
129
@end
130
 
131
@interface GULAppDelegateObserver : NSObject
132
@end
133
 
134
@implementation GULAppDelegateObserver {
135
  BOOL _isObserving;
136
}
137
 
138
+ (GULAppDelegateObserver *)sharedInstance {
139
  static GULAppDelegateObserver *instance;
140
  static dispatch_once_t once;
141
  dispatch_once(&once, ^{
142
    instance = [[GULAppDelegateObserver alloc] init];
143
  });
144
  return instance;
145
}
146
 
147
- (void)observeUIApplication {
148
  if (_isObserving) {
149
    return;
150
  }
151
  [[GULAppDelegateSwizzler sharedApplication]
152
      addObserver:self
153
       forKeyPath:kGULAppDelegateKeyPath
154
          options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
155
          context:nil];
156
  _isObserving = YES;
157
}
158
 
159
- (void)observeValueForKeyPath:(NSString *)keyPath
160
                      ofObject:(id)object
161
                        change:(NSDictionary *)change
162
                       context:(void *)context {
163
  if ([keyPath isEqual:kGULAppDelegateKeyPath]) {
164
    id newValue = change[NSKeyValueChangeNewKey];
165
    id oldValue = change[NSKeyValueChangeOldKey];
166
    if ([newValue isEqual:oldValue]) {
167
      return;
168
    }
169
    // Free the stored app delegate instance because it has been changed to a different instance to
170
    // avoid keeping it alive forever.
171
    if ([oldValue isEqual:gOriginalAppDelegate]) {
172
      gOriginalAppDelegate = nil;
173
      // Remove the observer. Parse it to NSObject to avoid warning.
174
      [[GULAppDelegateSwizzler sharedApplication] removeObserver:self
175
                                                      forKeyPath:kGULAppDelegateKeyPath];
176
      _isObserving = NO;
177
    }
178
  }
179
}
180
 
181
@end
182
 
183
@implementation GULAppDelegateSwizzler
184
 
185
static dispatch_once_t sProxyAppDelegateOnceToken;
186
static dispatch_once_t sProxyAppDelegateRemoteNotificationOnceToken;
187
 
188
#pragma mark - Public methods
189
 
190
+ (BOOL)isAppDelegateProxyEnabled {
191
  NSDictionary *infoDictionary = [NSBundle mainBundle].infoDictionary;
192
 
193
  id isFirebaseProxyEnabledPlistValue = infoDictionary[kGULFirebaseAppDelegateProxyEnabledPlistKey];
194
  id isGoogleProxyEnabledPlistValue =
195
      infoDictionary[kGULGoogleUtilitiesAppDelegateProxyEnabledPlistKey];
196
 
197
  // Enabled by default.
198
  BOOL isFirebaseAppDelegateProxyEnabled = YES;
199
  BOOL isGoogleUtilitiesAppDelegateProxyEnabled = YES;
200
 
201
  if ([isFirebaseProxyEnabledPlistValue isKindOfClass:[NSNumber class]]) {
202
    isFirebaseAppDelegateProxyEnabled = [isFirebaseProxyEnabledPlistValue boolValue];
203
  }
204
 
205
  if ([isGoogleProxyEnabledPlistValue isKindOfClass:[NSNumber class]]) {
206
    isGoogleUtilitiesAppDelegateProxyEnabled = [isGoogleProxyEnabledPlistValue boolValue];
207
  }
208
 
209
  // Only deactivate the proxy if it is explicitly disabled by app developers using either one of
210
  // the plist flags.
211
  return isFirebaseAppDelegateProxyEnabled && isGoogleUtilitiesAppDelegateProxyEnabled;
212
}
213
 
214
+ (GULAppDelegateInterceptorID)registerAppDelegateInterceptor:
215
    (id<GULApplicationDelegate>)interceptor {
216
  NSAssert(interceptor, @"AppDelegateProxy cannot add nil interceptor");
217
  NSAssert([interceptor conformsToProtocol:@protocol(GULApplicationDelegate)],
218
           @"AppDelegateProxy interceptor does not conform to UIApplicationDelegate");
219
 
220
  if (!interceptor) {
221
    GULLogError(kGULLoggerSwizzler, NO,
222
                [NSString stringWithFormat:@"I-SWZ%06ld",
223
                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling000],
224
                @"AppDelegateProxy cannot add nil interceptor.");
225
    return nil;
226
  }
227
  if (![interceptor conformsToProtocol:@protocol(GULApplicationDelegate)]) {
228
    GULLogError(kGULLoggerSwizzler, NO,
229
                [NSString stringWithFormat:@"I-SWZ%06ld",
230
                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling001],
231
                @"AppDelegateProxy interceptor does not conform to UIApplicationDelegate");
232
    return nil;
233
  }
234
 
235
  // The ID should be the same given the same interceptor object.
236
  NSString *interceptorID = [NSString stringWithFormat:@"%@%p", kGULAppDelegatePrefix, interceptor];
237
  if (!interceptorID.length) {
238
    GULLogError(kGULLoggerSwizzler, NO,
239
                [NSString stringWithFormat:@"I-SWZ%06ld",
240
                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling002],
241
                @"AppDelegateProxy cannot create Interceptor ID.");
242
    return nil;
243
  }
244
  GULZeroingWeakContainer *weakObject = [[GULZeroingWeakContainer alloc] init];
245
  weakObject.object = interceptor;
246
  [GULAppDelegateSwizzler interceptors][interceptorID] = weakObject;
247
  return interceptorID;
248
}
249
 
250
+ (void)unregisterAppDelegateInterceptorWithID:(GULAppDelegateInterceptorID)interceptorID {
251
  NSAssert(interceptorID, @"AppDelegateProxy cannot unregister nil interceptor ID.");
252
  NSAssert(((NSString *)interceptorID).length != 0,
253
           @"AppDelegateProxy cannot unregister empty interceptor ID.");
254
 
255
  if (!interceptorID) {
256
    GULLogError(kGULLoggerSwizzler, NO,
257
                [NSString stringWithFormat:@"I-SWZ%06ld",
258
                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling003],
259
                @"AppDelegateProxy cannot unregister empty interceptor ID.");
260
    return;
261
  }
262
 
263
  GULZeroingWeakContainer *weakContainer = [GULAppDelegateSwizzler interceptors][interceptorID];
264
  if (!weakContainer.object) {
265
    GULLogError(kGULLoggerSwizzler, NO,
266
                [NSString stringWithFormat:@"I-SWZ%06ld",
267
                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling004],
268
                @"AppDelegateProxy cannot unregister interceptor that was not registered. "
269
                 "Interceptor ID %@",
270
                interceptorID);
271
    return;
272
  }
273
 
274
  [[GULAppDelegateSwizzler interceptors] removeObjectForKey:interceptorID];
275
}
276
 
277
+ (void)proxyOriginalDelegate {
278
  if ([GULAppEnvironmentUtil isAppExtension]) {
279
    return;
280
  }
281
 
282
  dispatch_once(&sProxyAppDelegateOnceToken, ^{
283
    id<GULApplicationDelegate> originalDelegate =
284
        [GULAppDelegateSwizzler sharedApplication].delegate;
285
    [GULAppDelegateSwizzler proxyAppDelegate:originalDelegate];
286
  });
287
}
288
 
289
+ (void)proxyOriginalDelegateIncludingAPNSMethods {
290
  if ([GULAppEnvironmentUtil isAppExtension]) {
291
    return;
292
  }
293
 
294
  [self proxyOriginalDelegate];
295
 
296
  dispatch_once(&sProxyAppDelegateRemoteNotificationOnceToken, ^{
297
    id<GULApplicationDelegate> appDelegate = [GULAppDelegateSwizzler sharedApplication].delegate;
298
 
299
    NSMutableDictionary *realImplementationsBySelector =
300
        [objc_getAssociatedObject(appDelegate, &kGULRealIMPBySelectorKey) mutableCopy];
301
 
302
    [self proxyRemoteNotificationsMethodsWithAppDelegateSubClass:gAppDelegateSubclass
303
                                                       realClass:gOriginalAppDelegateClass
304
                                                     appDelegate:appDelegate
305
                                   realImplementationsBySelector:realImplementationsBySelector];
306
 
307
    objc_setAssociatedObject(appDelegate, &kGULRealIMPBySelectorKey,
308
                             [realImplementationsBySelector copy], OBJC_ASSOCIATION_RETAIN);
309
    [self reassignAppDelegate];
310
  });
311
}
312
 
313
#pragma mark - Create proxy
314
 
315
+ (GULApplication *)sharedApplication {
316
  if ([GULAppEnvironmentUtil isAppExtension]) {
317
    return nil;
318
  }
319
  id sharedApplication = nil;
320
  Class uiApplicationClass = NSClassFromString(kGULApplicationClassName);
321
  if (uiApplicationClass &&
322
      [uiApplicationClass respondsToSelector:(NSSelectorFromString(@"sharedApplication"))]) {
323
    sharedApplication = [uiApplicationClass sharedApplication];
324
  }
325
  return sharedApplication;
326
}
327
 
328
#pragma mark - Override default methods
329
 
330
/** Creates a new subclass of the class of the given object and sets the isa value of the given
331
 *  object to the new subclass. Additionally this copies methods to that new subclass that allow us
332
 *  to intercept UIApplicationDelegate methods. This is better known as isa swizzling.
333
 *
334
 *  @param appDelegate The object to which you want to isa swizzle. This has to conform to the
335
 *      UIApplicationDelegate subclass.
336
 *  @return Returns the new subclass.
337
 */
338
+ (nullable Class)createSubclassWithObject:(id<GULApplicationDelegate>)appDelegate {
339
  Class realClass = [appDelegate class];
340
 
341
  // Create GUL_<RealAppDelegate>_<UUID>
342
  NSString *classNameWithPrefix =
343
      [kGULAppDelegatePrefix stringByAppendingString:NSStringFromClass(realClass)];
344
  NSString *newClassName =
345
      [NSString stringWithFormat:@"%@-%@", classNameWithPrefix, [NSUUID UUID].UUIDString];
346
 
347
  if (NSClassFromString(newClassName)) {
348
    GULLogError(kGULLoggerSwizzler, NO,
349
                [NSString stringWithFormat:@"I-SWZ%06ld",
350
                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling005],
351
                @"Cannot create a proxy for App Delegate. Subclass already exists. Original Class: "
352
                @"%@, subclass: %@",
353
                NSStringFromClass(realClass), newClassName);
354
    return nil;
355
  }
356
 
357
  // Register the new class as subclass of the real one. Do not allocate more than the real class
358
  // size.
359
  Class appDelegateSubClass = objc_allocateClassPair(realClass, newClassName.UTF8String, 0);
360
  if (appDelegateSubClass == Nil) {
361
    GULLogError(kGULLoggerSwizzler, NO,
362
                [NSString stringWithFormat:@"I-SWZ%06ld",
363
                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling006],
364
                @"Cannot create a proxy for App Delegate. Subclass already exists. Original Class: "
365
                @"%@, subclass: Nil",
366
                NSStringFromClass(realClass));
367
    return nil;
368
  }
369
 
370
  NSMutableDictionary<NSString *, NSValue *> *realImplementationsBySelector =
371
      [[NSMutableDictionary alloc] init];
372
 
373
  // For application:continueUserActivity:restorationHandler:
374
  SEL continueUserActivitySEL = @selector(application:continueUserActivity:restorationHandler:);
375
  [self proxyDestinationSelector:continueUserActivitySEL
376
      implementationsFromSourceSelector:continueUserActivitySEL
377
                              fromClass:[GULAppDelegateSwizzler class]
378
                                toClass:appDelegateSubClass
379
                              realClass:realClass
380
       storeDestinationImplementationTo:realImplementationsBySelector];
381
 
382
#if TARGET_OS_IOS || TARGET_OS_TV
383
  // Add the following methods from GULAppDelegate class, and store the real implementation so it
384
  // can forward to the real one.
385
  // For application:openURL:options:
386
  SEL applicationOpenURLOptionsSEL = @selector(application:openURL:options:);
387
  if ([appDelegate respondsToSelector:applicationOpenURLOptionsSEL]) {
388
    // Only add the application:openURL:options: method if the original AppDelegate implements it.
389
    // This fixes a bug if an app only implements application:openURL:sourceApplication:annotation:
390
    // (if we add the `options` method, iOS sees that one exists and does not call the
391
    // `sourceApplication` method, which in this case is the only one the app implements).
392
 
393
    [self proxyDestinationSelector:applicationOpenURLOptionsSEL
394
        implementationsFromSourceSelector:applicationOpenURLOptionsSEL
395
                                fromClass:[GULAppDelegateSwizzler class]
396
                                  toClass:appDelegateSubClass
397
                                realClass:realClass
398
         storeDestinationImplementationTo:realImplementationsBySelector];
399
  }
400
 
401
  // For application:handleEventsForBackgroundURLSession:completionHandler:
402
  SEL handleEventsForBackgroundURLSessionSEL = @selector(application:
403
                                 handleEventsForBackgroundURLSession:completionHandler:);
404
  [self proxyDestinationSelector:handleEventsForBackgroundURLSessionSEL
405
      implementationsFromSourceSelector:handleEventsForBackgroundURLSessionSEL
406
                              fromClass:[GULAppDelegateSwizzler class]
407
                                toClass:appDelegateSubClass
408
                              realClass:realClass
409
       storeDestinationImplementationTo:realImplementationsBySelector];
410
#endif  // TARGET_OS_IOS || TARGET_OS_TV
411
 
412
#if TARGET_OS_IOS
413
  // For application:openURL:sourceApplication:annotation:
414
  SEL openURLSourceApplicationAnnotationSEL = @selector(application:
415
                                                            openURL:sourceApplication:annotation:);
416
 
417
  [self proxyDestinationSelector:openURLSourceApplicationAnnotationSEL
418
      implementationsFromSourceSelector:openURLSourceApplicationAnnotationSEL
419
                              fromClass:[GULAppDelegateSwizzler class]
420
                                toClass:appDelegateSubClass
421
                              realClass:realClass
422
       storeDestinationImplementationTo:realImplementationsBySelector];
423
#endif  // TARGET_OS_IOS
424
 
425
  // Override the description too so the custom class name will not show up.
426
  [GULAppDelegateSwizzler addInstanceMethodWithDestinationSelector:@selector(description)
427
                              withImplementationFromSourceSelector:@selector(fakeDescription)
428
                                                         fromClass:[self class]
429
                                                           toClass:appDelegateSubClass];
430
 
431
  // Store original implementations to a fake property of the original delegate.
432
  objc_setAssociatedObject(appDelegate, &kGULRealIMPBySelectorKey,
433
                           [realImplementationsBySelector copy], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
434
  objc_setAssociatedObject(appDelegate, &kGULRealClassKey, realClass,
435
                           OBJC_ASSOCIATION_RETAIN_NONATOMIC);
436
 
437
  // The subclass size has to be exactly the same size with the original class size. The subclass
438
  // cannot have more ivars/properties than its superclass since it will cause an offset in memory
439
  // that can lead to overwriting the isa of an object in the next frame.
440
  if (class_getInstanceSize(realClass) != class_getInstanceSize(appDelegateSubClass)) {
441
    GULLogError(kGULLoggerSwizzler, NO,
442
                [NSString stringWithFormat:@"I-SWZ%06ld",
443
                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling007],
444
                @"Cannot create subclass of App Delegate, because the created subclass is not the "
445
                @"same size. %@",
446
                NSStringFromClass(realClass));
447
    NSAssert(NO, @"Classes must be the same size to swizzle isa");
448
    return nil;
449
  }
450
 
451
  // Make the newly created class to be the subclass of the real App Delegate class.
452
  objc_registerClassPair(appDelegateSubClass);
453
  if (object_setClass(appDelegate, appDelegateSubClass)) {
454
    GULLogDebug(kGULLoggerSwizzler, NO,
455
                [NSString stringWithFormat:@"I-SWZ%06ld",
456
                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling008],
457
                @"Successfully created App Delegate Proxy automatically. To disable the "
458
                @"proxy, set the flag %@ to NO (Boolean) in the Info.plist",
459
                [GULAppDelegateSwizzler correctAppDelegateProxyKey]);
460
  }
461
 
462
  return appDelegateSubClass;
463
}
464
 
465
+ (void)proxyRemoteNotificationsMethodsWithAppDelegateSubClass:(Class)appDelegateSubClass
466
                                                     realClass:(Class)realClass
467
                                                   appDelegate:(id)appDelegate
468
                                 realImplementationsBySelector:
469
                                     (NSMutableDictionary *)realImplementationsBySelector {
470
  if (realClass == nil || appDelegateSubClass == nil || appDelegate == nil ||
471
      realImplementationsBySelector == nil) {
472
    // The App Delegate has not been swizzled.
473
    return;
474
  }
475
 
476
  // For application:didRegisterForRemoteNotificationsWithDeviceToken:
477
  SEL didRegisterForRemoteNotificationsSEL =
478
      NSSelectorFromString(kGULDidRegisterForRemoteNotificationsSEL);
479
  SEL didRegisterForRemoteNotificationsDonorSEL = @selector(application:
480
                 donor_didRegisterForRemoteNotificationsWithDeviceToken:);
481
 
482
  [self proxyDestinationSelector:didRegisterForRemoteNotificationsSEL
483
      implementationsFromSourceSelector:didRegisterForRemoteNotificationsDonorSEL
484
                              fromClass:[GULAppDelegateSwizzler class]
485
                                toClass:appDelegateSubClass
486
                              realClass:realClass
487
       storeDestinationImplementationTo:realImplementationsBySelector];
488
 
489
  // For application:didFailToRegisterForRemoteNotificationsWithError:
490
  SEL didFailToRegisterForRemoteNotificationsSEL =
491
      NSSelectorFromString(kGULDidFailToRegisterForRemoteNotificationsSEL);
492
  SEL didFailToRegisterForRemoteNotificationsDonorSEL = @selector(application:
493
                       donor_didFailToRegisterForRemoteNotificationsWithError:);
494
 
495
  [self proxyDestinationSelector:didFailToRegisterForRemoteNotificationsSEL
496
      implementationsFromSourceSelector:didFailToRegisterForRemoteNotificationsDonorSEL
497
                              fromClass:[GULAppDelegateSwizzler class]
498
                                toClass:appDelegateSubClass
499
                              realClass:realClass
500
       storeDestinationImplementationTo:realImplementationsBySelector];
501
 
502
  // For application:didReceiveRemoteNotification:
503
  SEL didReceiveRemoteNotificationSEL = NSSelectorFromString(kGULDidReceiveRemoteNotificationSEL);
504
  SEL didReceiveRemoteNotificationDonotSEL = @selector(application:
505
                                donor_didReceiveRemoteNotification:);
506
 
507
  [self proxyDestinationSelector:didReceiveRemoteNotificationSEL
508
      implementationsFromSourceSelector:didReceiveRemoteNotificationDonotSEL
509
                              fromClass:[GULAppDelegateSwizzler class]
510
                                toClass:appDelegateSubClass
511
                              realClass:realClass
512
       storeDestinationImplementationTo:realImplementationsBySelector];
513
 
514
  // For application:didReceiveRemoteNotification:fetchCompletionHandler:
515
#if !TARGET_OS_WATCH && !TARGET_OS_OSX
516
  SEL didReceiveRemoteNotificationWithCompletionSEL =
517
      NSSelectorFromString(kGULDidReceiveRemoteNotificationWithCompletionSEL);
518
  SEL didReceiveRemoteNotificationWithCompletionDonorSEL =
519
      @selector(application:donor_didReceiveRemoteNotification:fetchCompletionHandler:);
520
  if ([appDelegate respondsToSelector:didReceiveRemoteNotificationWithCompletionSEL]) {
521
    // Only add the application:didReceiveRemoteNotification:fetchCompletionHandler: method if
522
    // the original AppDelegate implements it.
523
    // This fixes a bug if an app only implements application:didReceiveRemoteNotification:
524
    // (if we add the method with completion, iOS sees that one exists and does not call
525
    // the method without the completion, which in this case is the only one the app implements).
526
 
527
    [self proxyDestinationSelector:didReceiveRemoteNotificationWithCompletionSEL
528
        implementationsFromSourceSelector:didReceiveRemoteNotificationWithCompletionDonorSEL
529
                                fromClass:[GULAppDelegateSwizzler class]
530
                                  toClass:appDelegateSubClass
531
                                realClass:realClass
532
         storeDestinationImplementationTo:realImplementationsBySelector];
533
  }
534
#endif  // !TARGET_OS_WATCH && !TARGET_OS_OSX
535
}
536
 
537
/// We have to do this to invalidate the cache that caches the original respondsToSelector of
538
/// openURL handlers. Without this, it won't call the default implementations because the system
539
/// checks and caches them.
540
/// Register KVO only once. Otherwise, the observing method will be called as many times as
541
/// being registered.
542
+ (void)reassignAppDelegate {
543
#if !TARGET_OS_WATCH
544
  id<GULApplicationDelegate> delegate = [self sharedApplication].delegate;
545
  [self sharedApplication].delegate = nil;
546
  [self sharedApplication].delegate = delegate;
547
  gOriginalAppDelegate = delegate;
548
  [[GULAppDelegateObserver sharedInstance] observeUIApplication];
549
#endif
550
}
551
 
552
#pragma mark - Helper methods
553
 
554
+ (GULMutableDictionary *)interceptors {
555
  static dispatch_once_t onceToken;
556
  static GULMutableDictionary *sInterceptors;
557
  dispatch_once(&onceToken, ^{
558
    sInterceptors = [[GULMutableDictionary alloc] init];
559
  });
560
  return sInterceptors;
561
}
562
 
563
+ (nullable NSValue *)originalImplementationForSelector:(SEL)selector object:(id)object {
564
  NSDictionary *realImplementationBySelector =
565
      objc_getAssociatedObject(object, &kGULRealIMPBySelectorKey);
566
  return realImplementationBySelector[NSStringFromSelector(selector)];
567
}
568
 
569
+ (void)proxyDestinationSelector:(SEL)destinationSelector
570
    implementationsFromSourceSelector:(SEL)sourceSelector
571
                            fromClass:(Class)sourceClass
572
                              toClass:(Class)destinationClass
573
                            realClass:(Class)realClass
574
     storeDestinationImplementationTo:
575
         (NSMutableDictionary<NSString *, NSValue *> *)destinationImplementationsBySelector {
576
  [self addInstanceMethodWithDestinationSelector:destinationSelector
577
            withImplementationFromSourceSelector:sourceSelector
578
                                       fromClass:sourceClass
579
                                         toClass:destinationClass];
580
  IMP sourceImplementation =
581
      [GULAppDelegateSwizzler implementationOfMethodSelector:destinationSelector
582
                                                   fromClass:realClass];
583
  NSValue *sourceImplementationPointer = [NSValue valueWithPointer:sourceImplementation];
584
 
585
  NSString *destinationSelectorString = NSStringFromSelector(destinationSelector);
586
  destinationImplementationsBySelector[destinationSelectorString] = sourceImplementationPointer;
587
}
588
 
589
/** Copies a method identified by the methodSelector from one class to the other. After this method
590
 *  is called, performing [toClassInstance methodSelector] will be similar to calling
591
 *  [fromClassInstance methodSelector]. This method does nothing if toClass already has a method
592
 *  identified by methodSelector.
593
 *
594
 *  @param methodSelector The SEL that identifies both the method on the fromClass as well as the
595
 *      one on the toClass.
596
 *  @param fromClass The class from which a method is sourced.
597
 *  @param toClass The class to which the method is added. If the class already has a method with
598
 *      the same selector, this has no effect.
599
 */
600
+ (void)addInstanceMethodWithSelector:(SEL)methodSelector
601
                            fromClass:(Class)fromClass
602
                              toClass:(Class)toClass {
603
  [self addInstanceMethodWithDestinationSelector:methodSelector
604
            withImplementationFromSourceSelector:methodSelector
605
                                       fromClass:fromClass
606
                                         toClass:toClass];
607
}
608
 
609
/** Copies a method identified by the sourceSelector from the fromClass as a method for the
610
 *  destinationSelector on the toClass. After this method is called, performing
611
 *  [toClassInstance destinationSelector] will be similar to calling
612
 *  [fromClassInstance sourceSelector]. This method does nothing if toClass already has a method
613
 *  identified by destinationSelector.
614
 *
615
 *  @param destinationSelector The SEL that identifies the method on the toClass.
616
 *  @param sourceSelector The SEL that identifies the method on the fromClass.
617
 *  @param fromClass The class from which a method is sourced.
618
 *  @param toClass The class to which the method is added. If the class already has a method with
619
 *      the same selector, this has no effect.
620
 */
621
+ (void)addInstanceMethodWithDestinationSelector:(SEL)destinationSelector
622
            withImplementationFromSourceSelector:(SEL)sourceSelector
623
                                       fromClass:(Class)fromClass
624
                                         toClass:(Class)toClass {
625
  Method method = class_getInstanceMethod(fromClass, sourceSelector);
626
  IMP methodIMP = method_getImplementation(method);
627
  const char *types = method_getTypeEncoding(method);
628
  if (!class_addMethod(toClass, destinationSelector, methodIMP, types)) {
629
    GULLogWarning(kGULLoggerSwizzler, NO,
630
                  [NSString stringWithFormat:@"I-SWZ%06ld",
631
                                             (long)kGULSwizzlerMessageCodeAppDelegateSwizzling009],
632
                  @"Cannot copy method to destination selector %@ as it already exists",
633
                  NSStringFromSelector(destinationSelector));
634
  }
635
}
636
 
637
/** Gets the IMP of the instance method on the class identified by the selector.
638
 *
639
 *  @param selector The selector of which the IMP is to be fetched.
640
 *  @param aClass The class from which the IMP is to be fetched.
641
 *  @return The IMP of the instance method identified by selector and aClass.
642
 */
643
+ (IMP)implementationOfMethodSelector:(SEL)selector fromClass:(Class)aClass {
644
  Method aMethod = class_getInstanceMethod(aClass, selector);
645
  return method_getImplementation(aMethod);
646
}
647
 
648
/** Enumerates through all the interceptors and if they respond to a given selector, executes a
649
 *  GULAppDelegateInterceptorCallback with the interceptor.
650
 *
651
 *  @param methodSelector The SEL to check if an interceptor responds to.
652
 *  @param callback the GULAppDelegateInterceptorCallback.
653
 */
654
+ (void)notifyInterceptorsWithMethodSelector:(SEL)methodSelector
655
                                    callback:(GULAppDelegateInterceptorCallback)callback {
656
  if (!callback) {
657
    return;
658
  }
659
 
660
  NSDictionary *interceptors = [GULAppDelegateSwizzler interceptors].dictionary;
661
  [interceptors enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
662
    GULZeroingWeakContainer *interceptorContainer = obj;
663
    id interceptor = interceptorContainer.object;
664
    if (!interceptor) {
665
      GULLogWarning(
666
          kGULLoggerSwizzler, NO,
667
          [NSString
668
              stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeAppDelegateSwizzling010],
669
          @"AppDelegateProxy cannot find interceptor with ID %@. Removing the interceptor.", key);
670
      [[GULAppDelegateSwizzler interceptors] removeObjectForKey:key];
671
      return;
672
    }
673
    if ([interceptor respondsToSelector:methodSelector]) {
674
      callback(interceptor);
675
    }
676
  }];
677
}
678
 
679
// The methods below are donor methods which are added to the dynamic subclass of the App Delegate.
680
// They are called within the scope of the real App Delegate so |self| does not refer to the
681
// GULAppDelegateSwizzler instance but the real App Delegate instance.
682
 
683
#pragma mark - [Donor Methods] Overridden instance description method
684
 
685
- (NSString *)fakeDescription {
686
  Class realClass = objc_getAssociatedObject(self, &kGULRealClassKey);
687
  return [NSString stringWithFormat:@"<%@: %p>", realClass, self];
688
}
689
 
690
#pragma mark - [Donor Methods] URL overridden handler methods
691
#if TARGET_OS_IOS || TARGET_OS_TV
692
 
693
- (BOOL)application:(GULApplication *)application
694
            openURL:(NSURL *)url
695
            options:(NSDictionary<NSString *, id> *)options {
696
  SEL methodSelector = @selector(application:openURL:options:);
697
  // Call the real implementation if the real App Delegate has any.
698
  NSValue *openURLIMPPointer =
699
      [GULAppDelegateSwizzler originalImplementationForSelector:methodSelector object:self];
700
  GULRealOpenURLOptionsIMP openURLOptionsIMP = [openURLIMPPointer pointerValue];
701
 
702
  __block BOOL returnedValue = NO;
703
 
704
// This is needed to for the library to be warning free on iOS versions < 9.
705
#pragma clang diagnostic push
706
#pragma clang diagnostic ignored "-Wunguarded-availability"
707
  [GULAppDelegateSwizzler
708
      notifyInterceptorsWithMethodSelector:methodSelector
709
                                  callback:^(id<GULApplicationDelegate> interceptor) {
710
                                    returnedValue |= [interceptor application:application
711
                                                                      openURL:url
712
                                                                      options:options];
713
                                  }];
714
#pragma clang diagnostic pop
715
  if (openURLOptionsIMP) {
716
    returnedValue |= openURLOptionsIMP(self, methodSelector, application, url, options);
717
  }
718
  return returnedValue;
719
}
720
 
721
#endif  // TARGET_OS_IOS || TARGET_OS_TV
722
 
723
#if TARGET_OS_IOS
724
 
725
- (BOOL)application:(GULApplication *)application
726
              openURL:(NSURL *)url
727
    sourceApplication:(NSString *)sourceApplication
728
           annotation:(id)annotation {
729
  SEL methodSelector = @selector(application:openURL:sourceApplication:annotation:);
730
 
731
  // Call the real implementation if the real App Delegate has any.
732
  NSValue *openURLSourceAppAnnotationIMPPointer =
733
      [GULAppDelegateSwizzler originalImplementationForSelector:methodSelector object:self];
734
  GULRealOpenURLSourceApplicationAnnotationIMP openURLSourceApplicationAnnotationIMP =
735
      [openURLSourceAppAnnotationIMPPointer pointerValue];
736
 
737
  __block BOOL returnedValue = NO;
738
  [GULAppDelegateSwizzler
739
      notifyInterceptorsWithMethodSelector:methodSelector
740
                                  callback:^(id<GULApplicationDelegate> interceptor) {
741
#pragma clang diagnostic push
742
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
743
                                    returnedValue |= [interceptor application:application
744
                                                                      openURL:url
745
                                                            sourceApplication:sourceApplication
746
                                                                   annotation:annotation];
747
#pragma clang diagnostic pop
748
                                  }];
749
  if (openURLSourceApplicationAnnotationIMP) {
750
    returnedValue |= openURLSourceApplicationAnnotationIMP(self, methodSelector, application, url,
751
                                                           sourceApplication, annotation);
752
  }
753
  return returnedValue;
754
}
755
 
756
#endif  // TARGET_OS_IOS
757
 
758
#pragma mark - [Donor Methods] Network overridden handler methods
759
 
760
#if TARGET_OS_IOS || TARGET_OS_TV
761
 
762
#pragma clang diagnostic push
763
#pragma clang diagnostic ignored "-Wstrict-prototypes"
764
- (void)application:(GULApplication *)application
765
    handleEventsForBackgroundURLSession:(NSString *)identifier
766
                      completionHandler:(void (^)())completionHandler API_AVAILABLE(ios(7.0)) {
767
#pragma clang diagnostic pop
768
  SEL methodSelector = @selector(application:
769
         handleEventsForBackgroundURLSession:completionHandler:);
770
  NSValue *handleBackgroundSessionPointer =
771
      [GULAppDelegateSwizzler originalImplementationForSelector:methodSelector object:self];
772
  GULRealHandleEventsForBackgroundURLSessionIMP handleBackgroundSessionIMP =
773
      [handleBackgroundSessionPointer pointerValue];
774
 
775
  // Notify interceptors.
776
  [GULAppDelegateSwizzler
777
      notifyInterceptorsWithMethodSelector:methodSelector
778
                                  callback:^(id<GULApplicationDelegate> interceptor) {
779
                                    [interceptor application:application
780
                                        handleEventsForBackgroundURLSession:identifier
781
                                                          completionHandler:completionHandler];
782
                                  }];
783
  // Call the real implementation if the real App Delegate has any.
784
  if (handleBackgroundSessionIMP) {
785
    handleBackgroundSessionIMP(self, methodSelector, application, identifier, completionHandler);
786
  }
787
}
788
 
789
#endif  // TARGET_OS_IOS || TARGET_OS_TV
790
 
791
#pragma mark - [Donor Methods] User Activities overridden handler methods
792
 
793
- (BOOL)application:(GULApplication *)application
794
    continueUserActivity:(NSUserActivity *)userActivity
795
      restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {
796
  SEL methodSelector = @selector(application:continueUserActivity:restorationHandler:);
797
  NSValue *continueUserActivityIMPPointer =
798
      [GULAppDelegateSwizzler originalImplementationForSelector:methodSelector object:self];
799
  GULRealContinueUserActivityIMP continueUserActivityIMP =
800
      continueUserActivityIMPPointer.pointerValue;
801
 
802
  __block BOOL returnedValue = NO;
803
#if !TARGET_OS_WATCH
804
  [GULAppDelegateSwizzler
805
      notifyInterceptorsWithMethodSelector:methodSelector
806
                                  callback:^(id<GULApplicationDelegate> interceptor) {
807
                                    returnedValue |= [interceptor application:application
808
                                                         continueUserActivity:userActivity
809
                                                           restorationHandler:restorationHandler];
810
                                  }];
811
#endif
812
  // Call the real implementation if the real App Delegate has any.
813
  if (continueUserActivityIMP) {
814
    returnedValue |= continueUserActivityIMP(self, methodSelector, application, userActivity,
815
                                             restorationHandler);
816
  }
817
  return returnedValue;
818
}
819
 
820
#pragma mark - [Donor Methods] Remote Notifications
821
 
822
- (void)application:(GULApplication *)application
823
    donor_didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
824
  SEL methodSelector = NSSelectorFromString(kGULDidRegisterForRemoteNotificationsSEL);
825
 
826
  NSValue *didRegisterForRemoteNotificationsIMPPointer =
827
      [GULAppDelegateSwizzler originalImplementationForSelector:methodSelector object:self];
828
  GULRealDidRegisterForRemoteNotificationsIMP didRegisterForRemoteNotificationsIMP =
829
      [didRegisterForRemoteNotificationsIMPPointer pointerValue];
830
 
831
  // Notify interceptors.
832
  [GULAppDelegateSwizzler
833
      notifyInterceptorsWithMethodSelector:methodSelector
834
                                  callback:^(id<GULApplicationDelegate> interceptor) {
835
                                    NSInvocation *invocation = [GULAppDelegateSwizzler
836
                                        appDelegateInvocationForSelector:methodSelector];
837
                                    [invocation setTarget:interceptor];
838
                                    [invocation setSelector:methodSelector];
839
                                    [invocation setArgument:(void *)(&application) atIndex:2];
840
                                    [invocation setArgument:(void *)(&deviceToken) atIndex:3];
841
                                    [invocation invoke];
842
                                  }];
843
  // Call the real implementation if the real App Delegate has any.
844
  if (didRegisterForRemoteNotificationsIMP) {
845
    didRegisterForRemoteNotificationsIMP(self, methodSelector, application, deviceToken);
846
  }
847
}
848
 
849
- (void)application:(GULApplication *)application
850
    donor_didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
851
  SEL methodSelector = NSSelectorFromString(kGULDidFailToRegisterForRemoteNotificationsSEL);
852
  NSValue *didFailToRegisterForRemoteNotificationsIMPPointer =
853
      [GULAppDelegateSwizzler originalImplementationForSelector:methodSelector object:self];
854
  GULRealDidFailToRegisterForRemoteNotificationsIMP didFailToRegisterForRemoteNotificationsIMP =
855
      [didFailToRegisterForRemoteNotificationsIMPPointer pointerValue];
856
 
857
  // Notify interceptors.
858
  [GULAppDelegateSwizzler
859
      notifyInterceptorsWithMethodSelector:methodSelector
860
                                  callback:^(id<GULApplicationDelegate> interceptor) {
861
                                    NSInvocation *invocation = [GULAppDelegateSwizzler
862
                                        appDelegateInvocationForSelector:methodSelector];
863
                                    [invocation setTarget:interceptor];
864
                                    [invocation setSelector:methodSelector];
865
                                    [invocation setArgument:(void *)(&application) atIndex:2];
866
                                    [invocation setArgument:(void *)(&error) atIndex:3];
867
                                    [invocation invoke];
868
                                  }];
869
  // Call the real implementation if the real App Delegate has any.
870
  if (didFailToRegisterForRemoteNotificationsIMP) {
871
    didFailToRegisterForRemoteNotificationsIMP(self, methodSelector, application, error);
872
  }
873
}
874
 
875
#if !TARGET_OS_WATCH && !TARGET_OS_OSX
876
- (void)application:(GULApplication *)application
877
    donor_didReceiveRemoteNotification:(NSDictionary *)userInfo
878
                fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
879
  SEL methodSelector = NSSelectorFromString(kGULDidReceiveRemoteNotificationWithCompletionSEL);
880
  NSValue *didReceiveRemoteNotificationWithCompletionIMPPointer =
881
      [GULAppDelegateSwizzler originalImplementationForSelector:methodSelector object:self];
882
  GULRealDidReceiveRemoteNotificationWithCompletionIMP
883
      didReceiveRemoteNotificationWithCompletionIMP =
884
          [didReceiveRemoteNotificationWithCompletionIMPPointer pointerValue];
885
 
886
  dispatch_group_t __block callbackGroup = dispatch_group_create();
887
  NSMutableArray<NSNumber *> *__block fetchResults = [NSMutableArray array];
888
 
889
  void (^localCompletionHandler)(UIBackgroundFetchResult) =
890
      ^void(UIBackgroundFetchResult fetchResult) {
891
        [fetchResults addObject:[NSNumber numberWithInt:(int)fetchResult]];
892
        dispatch_group_leave(callbackGroup);
893
      };
894
 
895
  // Notify interceptors.
896
  [GULAppDelegateSwizzler
897
      notifyInterceptorsWithMethodSelector:methodSelector
898
                                  callback:^(id<GULApplicationDelegate> interceptor) {
899
                                    dispatch_group_enter(callbackGroup);
900
 
901
                                    NSInvocation *invocation = [GULAppDelegateSwizzler
902
                                        appDelegateInvocationForSelector:methodSelector];
903
                                    [invocation setTarget:interceptor];
904
                                    [invocation setSelector:methodSelector];
905
                                    [invocation setArgument:(void *)(&application) atIndex:2];
906
                                    [invocation setArgument:(void *)(&userInfo) atIndex:3];
907
                                    [invocation setArgument:(void *)(&localCompletionHandler)
908
                                                    atIndex:4];
909
                                    [invocation invoke];
910
                                  }];
911
  // Call the real implementation if the real App Delegate has any.
912
  if (didReceiveRemoteNotificationWithCompletionIMP) {
913
    dispatch_group_enter(callbackGroup);
914
 
915
    didReceiveRemoteNotificationWithCompletionIMP(self, methodSelector, application, userInfo,
916
                                                  localCompletionHandler);
917
  }
918
 
919
  dispatch_group_notify(callbackGroup, dispatch_get_main_queue(), ^() {
920
    BOOL allFetchesFailed = YES;
921
    BOOL anyFetchHasNewData = NO;
922
 
923
    for (NSNumber *oneResult in fetchResults) {
924
      UIBackgroundFetchResult result = oneResult.intValue;
925
 
926
      switch (result) {
927
        case UIBackgroundFetchResultNoData:
928
          allFetchesFailed = NO;
929
          break;
930
        case UIBackgroundFetchResultNewData:
931
          allFetchesFailed = NO;
932
          anyFetchHasNewData = YES;
933
          break;
934
        case UIBackgroundFetchResultFailed:
935
 
936
          break;
937
      }
938
    }
939
 
940
    UIBackgroundFetchResult finalFetchResult = UIBackgroundFetchResultNoData;
941
 
942
    if (allFetchesFailed) {
943
      finalFetchResult = UIBackgroundFetchResultFailed;
944
    } else if (anyFetchHasNewData) {
945
      finalFetchResult = UIBackgroundFetchResultNewData;
946
    } else {
947
      finalFetchResult = UIBackgroundFetchResultNoData;
948
    }
949
 
950
    completionHandler(finalFetchResult);
951
  });
952
}
953
#endif  // !TARGET_OS_WATCH && !TARGET_OS_OSX
954
 
955
- (void)application:(GULApplication *)application
956
    donor_didReceiveRemoteNotification:(NSDictionary *)userInfo {
957
  SEL methodSelector = NSSelectorFromString(kGULDidReceiveRemoteNotificationSEL);
958
  NSValue *didReceiveRemoteNotificationIMPPointer =
959
      [GULAppDelegateSwizzler originalImplementationForSelector:methodSelector object:self];
960
  GULRealDidReceiveRemoteNotificationIMP didReceiveRemoteNotificationIMP =
961
      [didReceiveRemoteNotificationIMPPointer pointerValue];
962
 
963
  // Notify interceptors.
964
#pragma clang diagnostic push
965
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
966
  [GULAppDelegateSwizzler
967
      notifyInterceptorsWithMethodSelector:methodSelector
968
                                  callback:^(id<GULApplicationDelegate> interceptor) {
969
                                    NSInvocation *invocation = [GULAppDelegateSwizzler
970
                                        appDelegateInvocationForSelector:methodSelector];
971
                                    [invocation setTarget:interceptor];
972
                                    [invocation setSelector:methodSelector];
973
                                    [invocation setArgument:(void *)(&application) atIndex:2];
974
                                    [invocation setArgument:(void *)(&userInfo) atIndex:3];
975
                                    [invocation invoke];
976
                                  }];
977
#pragma clang diagnostic pop
978
  // Call the real implementation if the real App Delegate has any.
979
  if (didReceiveRemoteNotificationIMP) {
980
    didReceiveRemoteNotificationIMP(self, methodSelector, application, userInfo);
981
  }
982
}
983
 
984
+ (nullable NSInvocation *)appDelegateInvocationForSelector:(SEL)selector {
985
  struct objc_method_description methodDescription =
986
      protocol_getMethodDescription(@protocol(GULApplicationDelegate), selector, NO, YES);
987
  if (methodDescription.types == NULL) {
988
    return nil;
989
  }
990
 
991
  NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:methodDescription.types];
992
  return [NSInvocation invocationWithMethodSignature:signature];
993
}
994
 
995
+ (void)proxyAppDelegate:(id<GULApplicationDelegate>)appDelegate {
996
  if (![appDelegate conformsToProtocol:@protocol(GULApplicationDelegate)]) {
997
    GULLogNotice(
998
        kGULLoggerSwizzler, NO,
999
        [NSString
1000
            stringWithFormat:@"I-SWZ%06ld",
1001
                             (long)kGULSwizzlerMessageCodeAppDelegateSwizzlingInvalidAppDelegate],
1002
        @"App Delegate does not conform to UIApplicationDelegate protocol. %@",
1003
        [GULAppDelegateSwizzler correctAlternativeWhenAppDelegateProxyNotCreated]);
1004
    return;
1005
  }
1006
 
1007
  id<GULApplicationDelegate> originalDelegate = appDelegate;
1008
  // Do not create a subclass if it is not enabled.
1009
  if (![GULAppDelegateSwizzler isAppDelegateProxyEnabled]) {
1010
    GULLogNotice(kGULLoggerSwizzler, NO,
1011
                 [NSString stringWithFormat:@"I-SWZ%06ld",
1012
                                            (long)kGULSwizzlerMessageCodeAppDelegateSwizzling011],
1013
                 @"App Delegate Proxy is disabled. %@",
1014
                 [GULAppDelegateSwizzler correctAlternativeWhenAppDelegateProxyNotCreated]);
1015
    return;
1016
  }
1017
  // Do not accept nil delegate.
1018
  if (!originalDelegate) {
1019
    GULLogError(kGULLoggerSwizzler, NO,
1020
                [NSString stringWithFormat:@"I-SWZ%06ld",
1021
                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling012],
1022
                @"Cannot create App Delegate Proxy because App Delegate instance is nil. %@",
1023
                [GULAppDelegateSwizzler correctAlternativeWhenAppDelegateProxyNotCreated]);
1024
    return;
1025
  }
1026
 
1027
  @try {
1028
    gOriginalAppDelegateClass = [originalDelegate class];
1029
    gAppDelegateSubclass = [self createSubclassWithObject:originalDelegate];
1030
    [self reassignAppDelegate];
1031
  } @catch (NSException *exception) {
1032
    GULLogError(kGULLoggerSwizzler, NO,
1033
                [NSString stringWithFormat:@"I-SWZ%06ld",
1034
                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling013],
1035
                @"Cannot create App Delegate Proxy. %@",
1036
                [GULAppDelegateSwizzler correctAlternativeWhenAppDelegateProxyNotCreated]);
1037
    return;
1038
  }
1039
}
1040
 
1041
#pragma mark - Methods to print correct debug logs
1042
 
1043
+ (NSString *)correctAppDelegateProxyKey {
1044
  return NSClassFromString(@"FIRCore") ? kGULFirebaseAppDelegateProxyEnabledPlistKey
1045
                                       : kGULGoogleUtilitiesAppDelegateProxyEnabledPlistKey;
1046
}
1047
 
1048
+ (NSString *)correctAlternativeWhenAppDelegateProxyNotCreated {
1049
  return NSClassFromString(@"FIRCore")
1050
             ? @"To log deep link campaigns manually, call the methods in "
1051
               @"FIRAnalytics+AppDelegate.h."
1052
             : @"";
1053
}
1054
 
1055
#pragma mark - Private Methods for Testing
1056
 
1057
+ (void)clearInterceptors {
1058
  [[self interceptors] removeAllObjects];
1059
}
1060
 
1061
+ (void)resetProxyOriginalDelegateOnceToken {
1062
  sProxyAppDelegateOnceToken = 0;
1063
  sProxyAppDelegateRemoteNotificationOnceToken = 0;
1064
}
1065
 
1066
+ (id<GULApplicationDelegate>)originalDelegate {
1067
  return gOriginalAppDelegate;
1068
}
1069
 
1070
@end