FIRAuthAppDelegateProxy.m 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "Private/FIRAuthAppDelegateProxy.h"
  17. #import <objc/runtime.h>
  18. NS_ASSUME_NONNULL_BEGIN
  19. /** @var kProxyEnabledBundleKey
  20. @brief The key in application's bundle plist for whether or not proxy should be enabled.
  21. @remarks This key is a shared constant with Analytics and FCM.
  22. */
  23. static NSString *const kProxyEnabledBundleKey = @"FirebaseAppDelegateProxyEnabled";
  24. /** @fn noop
  25. @brief A function that does nothing.
  26. @remarks This is used as the placeholder for unimplemented UApplicationDelegate methods,
  27. because once we added a method there is no way to remove it from the class.
  28. */
  29. #if !OBJC_OLD_DISPATCH_PROTOTYPES
  30. static void noop(void) {
  31. }
  32. #else
  33. static id noop(id object, SEL cmd, ...) {
  34. return nil;
  35. }
  36. #endif
  37. @implementation FIRAuthAppDelegateProxy {
  38. /** @var _appDelegate
  39. @brief The application delegate whose method is being swizzled.
  40. */
  41. id<UIApplicationDelegate> _appDelegate;
  42. /** @var _orginalImplementationsBySelector
  43. @brief A map from selectors to original implementations that have been swizzled.
  44. */
  45. NSMutableDictionary<NSValue *, NSValue *> *_originalImplementationsBySelector;
  46. /** @var _handlers
  47. @brief The array of weak pointers of `id<FIRAuthAppDelegateHandler>`.
  48. */
  49. NSPointerArray *_handlers;
  50. }
  51. - (nullable instancetype)initWithApplication:(nullable UIApplication *)application {
  52. self = [super init];
  53. if (self) {
  54. id proxyEnabled = [[NSBundle mainBundle] objectForInfoDictionaryKey:kProxyEnabledBundleKey];
  55. if ([proxyEnabled isKindOfClass:[NSNumber class]] && !((NSNumber *)proxyEnabled).boolValue) {
  56. return nil;
  57. }
  58. _appDelegate = application.delegate;
  59. if (![_appDelegate conformsToProtocol:@protocol(UIApplicationDelegate)]) {
  60. return nil;
  61. }
  62. _originalImplementationsBySelector = [[NSMutableDictionary<NSValue *, NSValue *> alloc] init];
  63. _handlers = [[NSPointerArray alloc] initWithOptions:NSPointerFunctionsWeakMemory];
  64. // Swizzle the methods.
  65. __weak FIRAuthAppDelegateProxy *weakSelf = self;
  66. SEL registerDeviceTokenSelector =
  67. @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:);
  68. [self replaceSelector:registerDeviceTokenSelector
  69. withBlock:^(id object, UIApplication* application, NSData *deviceToken) {
  70. [weakSelf object:object
  71. selector:registerDeviceTokenSelector
  72. application:application
  73. didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
  74. }];
  75. SEL receiveNotificationSelector = @selector(application:didReceiveRemoteNotification:);
  76. SEL receiveNotificationWithHandlerSelector =
  77. @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:);
  78. if ([_appDelegate respondsToSelector:receiveNotificationWithHandlerSelector] ||
  79. ![_appDelegate respondsToSelector:receiveNotificationSelector]) {
  80. // Replace the modern selector which is available on iOS 7 and above.
  81. [self replaceSelector:receiveNotificationWithHandlerSelector
  82. withBlock:^(id object, UIApplication *application, NSDictionary *notification,
  83. void (^completionHandler)(UIBackgroundFetchResult)) {
  84. [weakSelf object:object
  85. selector:receiveNotificationWithHandlerSelector
  86. application:application
  87. didReceiveRemoteNotification:notification
  88. fetchCompletionHandler:completionHandler];
  89. }];
  90. } else {
  91. // Replace the deprecated selector because this is the only one that the client app uses.
  92. [self replaceSelector:receiveNotificationSelector
  93. withBlock:^(id object, UIApplication *application, NSDictionary *notification) {
  94. [weakSelf object:object
  95. selector:receiveNotificationSelector
  96. application:application
  97. didReceiveRemoteNotification:notification];
  98. }];
  99. }
  100. }
  101. return self;
  102. }
  103. - (void)dealloc {
  104. for (NSValue *selector in _originalImplementationsBySelector) {
  105. IMP implementation = _originalImplementationsBySelector[selector].pointerValue;
  106. Method method = class_getInstanceMethod([_appDelegate class], selector.pointerValue);
  107. imp_removeBlock(method_setImplementation(method, implementation));
  108. }
  109. }
  110. - (void)addHandler:(__weak id<FIRAuthAppDelegateHandler>)handler {
  111. @synchronized (_handlers) {
  112. [_handlers addPointer:(__bridge void *)handler];
  113. }
  114. }
  115. + (nullable instancetype)sharedInstance {
  116. static dispatch_once_t onceToken;
  117. static FIRAuthAppDelegateProxy *_Nullable sharedInstance;
  118. dispatch_once(&onceToken, ^{
  119. sharedInstance = [[self alloc] initWithApplication:[UIApplication sharedApplication]];
  120. });
  121. return sharedInstance;
  122. }
  123. #pragma mark - UIApplicationDelegate proxy methods.
  124. - (void)object:(id)object
  125. selector:(SEL)selector
  126. application:(UIApplication *)application
  127. didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
  128. if (object == _appDelegate) {
  129. for (id<FIRAuthAppDelegateHandler> handler in [self handlers]) {
  130. [handler setAPNSToken:deviceToken];
  131. }
  132. }
  133. IMP originalImplementation = [self originalImplementationForSelector:selector];
  134. if (originalImplementation) {
  135. typedef void (*Implmentation)(id, SEL, UIApplication*, NSData *);
  136. ((Implmentation)originalImplementation)(object, selector, application, deviceToken);
  137. }
  138. }
  139. - (void)object:(id)object
  140. selector:(SEL)selector
  141. application:(UIApplication *)application
  142. didReceiveRemoteNotification:(NSDictionary *)notification
  143. fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  144. if (object == _appDelegate) {
  145. for (id<FIRAuthAppDelegateHandler> handler in [self handlers]) {
  146. if ([handler canHandleNotification:notification]) {
  147. completionHandler(UIBackgroundFetchResultNoData);
  148. return;
  149. };
  150. }
  151. }
  152. IMP originalImplementation = [self originalImplementationForSelector:selector];
  153. typedef void (*Implmentation)(id, SEL, UIApplication*, NSDictionary *,
  154. void (^)(UIBackgroundFetchResult));
  155. ((Implmentation)originalImplementation)(object, selector, application, notification,
  156. completionHandler);
  157. }
  158. - (void)object:(id)object
  159. selector:(SEL)selector
  160. application:(UIApplication *)application
  161. didReceiveRemoteNotification:(NSDictionary *)notification {
  162. if (object == _appDelegate) {
  163. for (id<FIRAuthAppDelegateHandler> handler in [self handlers]) {
  164. if ([handler canHandleNotification:notification]) {
  165. return;
  166. };
  167. }
  168. }
  169. IMP originalImplementation = [self originalImplementationForSelector:selector];
  170. typedef void (*Implmentation)(id, SEL, UIApplication*, NSDictionary *);
  171. ((Implmentation)originalImplementation)(object, selector, application, notification);
  172. }
  173. #pragma mark - Internal Methods
  174. /** @fn handlers
  175. @brief Gets the list of handlers from `_handlers` safely.
  176. */
  177. - (NSArray<id<FIRAuthAppDelegateHandler>> *)handlers {
  178. @synchronized (_handlers) {
  179. NSMutableArray<id<FIRAuthAppDelegateHandler>> *liveHandlers =
  180. [[NSMutableArray<id<FIRAuthAppDelegateHandler>> alloc] initWithCapacity:_handlers.count];
  181. for (__weak id<FIRAuthAppDelegateHandler> handler in _handlers) {
  182. if (handler) {
  183. [liveHandlers addObject:handler];
  184. }
  185. }
  186. if (liveHandlers.count < _handlers.count) {
  187. [_handlers compact];
  188. }
  189. return liveHandlers;
  190. }
  191. }
  192. /** @fn replaceSelector:withBlock:
  193. @brief replaces the implementation for a method of `_appDelegate` specified by a selector.
  194. @param selector The selector for the method.
  195. @param block The block as the new implementation of the method.
  196. */
  197. - (void)replaceSelector:(SEL)selector withBlock:(id)block {
  198. Method originalMethod = class_getInstanceMethod([_appDelegate class], selector);
  199. IMP newImplementation = imp_implementationWithBlock(block);
  200. IMP originalImplementation;
  201. if (originalMethod) {
  202. originalImplementation = method_setImplementation(originalMethod, newImplementation) ?: &noop;
  203. } else {
  204. // The original method was not implemented in the class, add it with the new implementation.
  205. struct objc_method_description methodDescription =
  206. protocol_getMethodDescription(@protocol(UIApplicationDelegate), selector, NO, YES);
  207. class_addMethod([_appDelegate class], selector, newImplementation, methodDescription.types);
  208. originalImplementation = &noop;
  209. }
  210. _originalImplementationsBySelector[[NSValue valueWithPointer:selector]] =
  211. [NSValue valueWithPointer:originalImplementation];
  212. }
  213. /** @fn originalImplementationForSelector:
  214. @brief Gets the original implementation for the given selector.
  215. @param selector The selector for the method that has been replaced.
  216. @return The original implementation if there was one.
  217. */
  218. - (IMP)originalImplementationForSelector:(SEL)selector {
  219. return _originalImplementationsBySelector[[NSValue valueWithPointer:selector]].pointerValue;
  220. }
  221. @end
  222. NS_ASSUME_NONNULL_END