暂无描述

GULAppDelegateSwizzler.m 48KB

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