FIRMessagingRemoteNotificationsProxy.m 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  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 "FIRMessagingRemoteNotificationsProxy.h"
  17. #import <objc/runtime.h>
  18. #import <UIKit/UIKit.h>
  19. #import "FIRMessagingConstants.h"
  20. #import "FIRMessagingLogger.h"
  21. #import "FIRMessaging_Private.h"
  22. static const BOOL kDefaultAutoRegisterEnabledValue = YES;
  23. static void * UserNotificationObserverContext = &UserNotificationObserverContext;
  24. static NSString *kUserNotificationWillPresentSelectorString =
  25. @"userNotificationCenter:willPresentNotification:withCompletionHandler:";
  26. static NSString *kUserNotificationDidReceiveResponseSelectorString =
  27. @"userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:";
  28. @interface FIRMessagingRemoteNotificationsProxy ()
  29. @property(strong, nonatomic) NSMutableDictionary<NSString *, NSValue *> *originalAppDelegateImps;
  30. @property(strong, nonatomic) NSMutableDictionary<NSString *, NSArray *> *swizzledSelectorsByClass;
  31. @property(nonatomic) BOOL didSwizzleMethods;
  32. @property(nonatomic) BOOL didSwizzleAppDelegateMethods;
  33. @property(nonatomic) BOOL hasSwizzledUserNotificationDelegate;
  34. @property(nonatomic) BOOL isObservingUserNotificationDelegateChanges;
  35. @property(strong, nonatomic) id userNotificationCenter;
  36. @property(strong, nonatomic) id currentUserNotificationCenterDelegate;
  37. @end
  38. @implementation FIRMessagingRemoteNotificationsProxy
  39. + (BOOL)canSwizzleMethods {
  40. id canSwizzleValue =
  41. [[NSBundle mainBundle]
  42. objectForInfoDictionaryKey: kFIRMessagingRemoteNotificationsProxyEnabledInfoPlistKey];
  43. if (canSwizzleValue && [canSwizzleValue isKindOfClass:[NSNumber class]]) {
  44. NSNumber *canSwizzleNumberValue = (NSNumber *)canSwizzleValue;
  45. return canSwizzleNumberValue.boolValue;
  46. } else {
  47. return kDefaultAutoRegisterEnabledValue;
  48. }
  49. }
  50. + (void)swizzleMethods {
  51. [[FIRMessagingRemoteNotificationsProxy sharedProxy] swizzleMethodsIfPossible];
  52. }
  53. + (instancetype)sharedProxy {
  54. static FIRMessagingRemoteNotificationsProxy *proxy;
  55. static dispatch_once_t onceToken;
  56. dispatch_once(&onceToken, ^{
  57. proxy = [[FIRMessagingRemoteNotificationsProxy alloc] init];
  58. });
  59. return proxy;
  60. }
  61. - (instancetype)init {
  62. self = [super init];
  63. if (self) {
  64. _originalAppDelegateImps = [[NSMutableDictionary alloc] init];
  65. _swizzledSelectorsByClass = [[NSMutableDictionary alloc] init];
  66. }
  67. return self;
  68. }
  69. - (void)dealloc {
  70. [self unswizzleAllMethods];
  71. self.swizzledSelectorsByClass = nil;
  72. [self.originalAppDelegateImps removeAllObjects];
  73. self.originalAppDelegateImps = nil;
  74. [self removeUserNotificationCenterDelegateObserver];
  75. }
  76. - (void)swizzleMethodsIfPossible {
  77. // Already swizzled.
  78. if (self.didSwizzleMethods) {
  79. return;
  80. }
  81. NSObject<UIApplicationDelegate> *appDelegate = [[UIApplication sharedApplication] delegate];
  82. [self swizzleAppDelegateMethods:appDelegate];
  83. // Add KVO listener on [UNUserNotificationCenter currentNotificationCenter]'s delegate property
  84. Class notificationCenterClass = NSClassFromString(@"UNUserNotificationCenter");
  85. if (notificationCenterClass) {
  86. // We are linked against iOS 10 SDK or above
  87. id notificationCenter = getNamedPropertyFromObject(notificationCenterClass,
  88. @"currentNotificationCenter",
  89. notificationCenterClass);
  90. if (notificationCenter) {
  91. [self listenForDelegateChangesInUserNotificationCenter:notificationCenter];
  92. }
  93. }
  94. self.didSwizzleMethods = YES;
  95. }
  96. - (void)unswizzleAllMethods {
  97. for (NSString *className in self.swizzledSelectorsByClass) {
  98. Class klass = NSClassFromString(className);
  99. NSArray *selectorStrings = self.swizzledSelectorsByClass[className];
  100. for (NSString *selectorString in selectorStrings) {
  101. SEL selector = NSSelectorFromString(selectorString);
  102. [self unswizzleSelector:selector inClass:klass];
  103. }
  104. }
  105. [self.swizzledSelectorsByClass removeAllObjects];
  106. }
  107. - (void)swizzleAppDelegateMethods:(id<UIApplicationDelegate>)appDelegate {
  108. if (![appDelegate conformsToProtocol:@protocol(UIApplicationDelegate)]) {
  109. return;
  110. }
  111. Class appDelegateClass = [appDelegate class];
  112. BOOL didSwizzleAppDelegate = NO;
  113. // Message receiving handler for iOS 9, 8, 7 devices (both display notification and data message).
  114. SEL remoteNotificationSelector =
  115. @selector(application:didReceiveRemoteNotification:);
  116. SEL remoteNotificationWithFetchHandlerSelector =
  117. @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:);
  118. // For data message from MCS.
  119. SEL receiveDataMessageSelector = NSSelectorFromString(@"applicationReceivedRemoteMessage:");
  120. // For recording when APNS tokens are registered (or fail to register)
  121. SEL registerForAPNSFailSelector =
  122. @selector(application:didFailToRegisterForRemoteNotificationsWithError:);
  123. SEL registerForAPNSSuccessSelector =
  124. @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:);
  125. // Receive Remote Notifications.
  126. BOOL selectorWithFetchHandlerImplemented = NO;
  127. if ([appDelegate respondsToSelector:remoteNotificationWithFetchHandlerSelector]) {
  128. selectorWithFetchHandlerImplemented = YES;
  129. [self swizzleSelector:remoteNotificationWithFetchHandlerSelector
  130. inClass:appDelegateClass
  131. withImplementation:(IMP)FCM_swizzle_appDidReceiveRemoteNotificationWithHandler
  132. inProtocol:@protocol(UIApplicationDelegate)];
  133. didSwizzleAppDelegate = YES;
  134. }
  135. if ([appDelegate respondsToSelector:remoteNotificationSelector] ||
  136. !selectorWithFetchHandlerImplemented) {
  137. [self swizzleSelector:remoteNotificationSelector
  138. inClass:appDelegateClass
  139. withImplementation:(IMP)FCM_swizzle_appDidReceiveRemoteNotification
  140. inProtocol:@protocol(UIApplicationDelegate)];
  141. didSwizzleAppDelegate = YES;
  142. }
  143. if ([appDelegate respondsToSelector:receiveDataMessageSelector]) {
  144. [self swizzleSelector:receiveDataMessageSelector
  145. inClass:appDelegateClass
  146. withImplementation:(IMP)FCM_swizzle_applicationReceivedRemoteMessage
  147. inProtocol:@protocol(UIApplicationDelegate)];
  148. didSwizzleAppDelegate = YES;
  149. }
  150. // Receive APNS token
  151. [self swizzleSelector:registerForAPNSSuccessSelector
  152. inClass:appDelegateClass
  153. withImplementation:(IMP)FCM_swizzle_appDidRegisterForRemoteNotifications
  154. inProtocol:@protocol(UIApplicationDelegate)];
  155. [self swizzleSelector:registerForAPNSFailSelector
  156. inClass:appDelegateClass
  157. withImplementation:(IMP)FCM_swizzle_appDidFailToRegisterForRemoteNotifications
  158. inProtocol:@protocol(UIApplicationDelegate)];
  159. self.didSwizzleAppDelegateMethods = didSwizzleAppDelegate;
  160. }
  161. - (void)listenForDelegateChangesInUserNotificationCenter:(id)notificationCenter {
  162. Class notificationCenterClass = NSClassFromString(@"UNUserNotificationCenter");
  163. if (![notificationCenter isKindOfClass:notificationCenterClass]) {
  164. return;
  165. }
  166. id delegate = getNamedPropertyFromObject(notificationCenter, @"delegate", nil);
  167. Protocol *delegateProtocol = NSProtocolFromString(@"UNUserNotificationCenterDelegate");
  168. if ([delegate conformsToProtocol:delegateProtocol]) {
  169. // Swizzle this object now, if available
  170. [self swizzleUserNotificationCenterDelegate:delegate];
  171. }
  172. // Add KVO observer for "delegate" keyPath for future changes
  173. [self addDelegateObserverToUserNotificationCenter:notificationCenter];
  174. }
  175. #pragma mark - UNNotificationCenter Swizzling
  176. - (void)swizzleUserNotificationCenterDelegate:(id)delegate {
  177. if (self.currentUserNotificationCenterDelegate == delegate) {
  178. // Via pointer-check, compare if we have already swizzled this item.
  179. return;
  180. }
  181. Protocol *userNotificationCenterProtocol =
  182. NSProtocolFromString(@"UNUserNotificationCenterDelegate");
  183. if ([delegate conformsToProtocol:userNotificationCenterProtocol]) {
  184. SEL willPresentNotificationSelector =
  185. NSSelectorFromString(kUserNotificationWillPresentSelectorString);
  186. // Swizzle the optional method
  187. // "userNotificationCenter:willPresentNotification:withCompletionHandler:", if it is
  188. // implemented. Do not swizzle otherwise, as an implementation *will* be created, which will
  189. // fool iOS into thinking that this method is implemented, and therefore not send notifications
  190. // to the fallback method in the app delegate
  191. // "application:didReceiveRemoteNotification:fetchCompletionHandler:".
  192. if ([delegate respondsToSelector:willPresentNotificationSelector]) {
  193. [self swizzleSelector:willPresentNotificationSelector
  194. inClass:[delegate class]
  195. withImplementation:(IMP)FCM_swizzle_willPresentNotificationWithHandler
  196. inProtocol:userNotificationCenterProtocol];
  197. }
  198. SEL didReceiveNotificationResponseSelector =
  199. NSSelectorFromString(kUserNotificationDidReceiveResponseSelectorString);
  200. if ([delegate respondsToSelector:didReceiveNotificationResponseSelector]) {
  201. [self swizzleSelector:didReceiveNotificationResponseSelector
  202. inClass:[delegate class]
  203. withImplementation:(IMP)FCM_swizzle_didReceiveNotificationResponseWithHandler
  204. inProtocol:userNotificationCenterProtocol];
  205. }
  206. self.currentUserNotificationCenterDelegate = delegate;
  207. self.hasSwizzledUserNotificationDelegate = YES;
  208. }
  209. }
  210. - (void)unswizzleUserNotificationCenterDelegate:(id)delegate {
  211. if (self.currentUserNotificationCenterDelegate != delegate) {
  212. // We aren't swizzling this delegate, so don't do anything.
  213. return;
  214. }
  215. SEL willPresentNotificationSelector =
  216. NSSelectorFromString(kUserNotificationWillPresentSelectorString);
  217. // Call unswizzle methods, even if the method was not implemented (it will fail gracefully).
  218. [self unswizzleSelector:willPresentNotificationSelector
  219. inClass:[self.currentUserNotificationCenterDelegate class]];
  220. self.currentUserNotificationCenterDelegate = nil;
  221. self.hasSwizzledUserNotificationDelegate = NO;
  222. }
  223. #pragma mark - KVO for UNUserNotificationCenter
  224. - (void)addDelegateObserverToUserNotificationCenter:(id)userNotificationCenter {
  225. [self removeUserNotificationCenterDelegateObserver];
  226. @try {
  227. [userNotificationCenter addObserver:self
  228. forKeyPath:NSStringFromSelector(@selector(delegate))
  229. options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
  230. context:UserNotificationObserverContext];
  231. self.userNotificationCenter = userNotificationCenter;
  232. self.isObservingUserNotificationDelegateChanges = YES;
  233. } @catch (NSException *exception) {
  234. FIRMessagingLoggerError(kFIRMessagingMessageCodeRemoteNotificationsProxy000,
  235. @"Encountered exception trying to add a KVO observer for "
  236. @"UNUserNotificationCenter's 'delegate' property: %@",
  237. exception);
  238. } @finally {
  239. }
  240. }
  241. - (void)removeUserNotificationCenterDelegateObserver {
  242. if (!self.userNotificationCenter) {
  243. return;
  244. }
  245. @try {
  246. [self.userNotificationCenter removeObserver:self
  247. forKeyPath:NSStringFromSelector(@selector(delegate))
  248. context:UserNotificationObserverContext];
  249. self.userNotificationCenter = nil;
  250. self.isObservingUserNotificationDelegateChanges = NO;
  251. } @catch (NSException *exception) {
  252. FIRMessagingLoggerError(kFIRMessagingMessageCodeRemoteNotificationsProxy001,
  253. @"Encountered exception trying to remove a KVO observer for "
  254. @"UNUserNotificationCenter's 'delegate' property: %@",
  255. exception);
  256. } @finally {
  257. }
  258. }
  259. - (void)observeValueForKeyPath:(NSString *)keyPath
  260. ofObject:(id)object
  261. change:(NSDictionary<NSKeyValueChangeKey, id> *)change
  262. context:(void *)context {
  263. if (context == UserNotificationObserverContext) {
  264. if ([keyPath isEqualToString:NSStringFromSelector(@selector(delegate))]) {
  265. id oldDelegate = change[NSKeyValueChangeOldKey];
  266. if (oldDelegate && oldDelegate != [NSNull null]) {
  267. [self unswizzleUserNotificationCenterDelegate:oldDelegate];
  268. }
  269. id newDelegate = change[NSKeyValueChangeNewKey];
  270. if (newDelegate && newDelegate != [NSNull null]) {
  271. [self swizzleUserNotificationCenterDelegate:newDelegate];
  272. }
  273. }
  274. } else {
  275. [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
  276. }
  277. }
  278. #pragma mark - NSProxy methods
  279. - (void)saveOriginalImplementation:(IMP)imp forSelector:(SEL)selector {
  280. if (imp && selector) {
  281. NSValue *IMPValue = [NSValue valueWithPointer:imp];
  282. NSString *selectorString = NSStringFromSelector(selector);
  283. self.originalAppDelegateImps[selectorString] = IMPValue;
  284. }
  285. }
  286. - (IMP)originalImplementationForSelector:(SEL)selector {
  287. NSString *selectorString = NSStringFromSelector(selector);
  288. NSValue *implementation_value = self.originalAppDelegateImps[selectorString];
  289. if (!implementation_value) {
  290. return nil;
  291. }
  292. IMP imp;
  293. [implementation_value getValue:&imp];
  294. return imp;
  295. }
  296. - (void)trackSwizzledSelector:(SEL)selector ofClass:(Class)klass {
  297. NSString *className = NSStringFromClass(klass);
  298. NSString *selectorString = NSStringFromSelector(selector);
  299. NSArray *selectors = self.swizzledSelectorsByClass[selectorString];
  300. if (selectors) {
  301. selectors = [selectors arrayByAddingObject:selectorString];
  302. } else {
  303. selectors = @[selectorString];
  304. }
  305. self.swizzledSelectorsByClass[className] = selectors;
  306. }
  307. - (void)removeImplementationForSelector:(SEL)selector {
  308. NSString *selectorString = NSStringFromSelector(selector);
  309. [self.originalAppDelegateImps removeObjectForKey:selectorString];
  310. }
  311. - (void)swizzleSelector:(SEL)originalSelector
  312. inClass:(Class)klass
  313. withImplementation:(IMP)swizzledImplementation
  314. inProtocol:(Protocol *)protocol {
  315. Method originalMethod = class_getInstanceMethod(klass, originalSelector);
  316. if (originalMethod) {
  317. // This class implements this method, so replace the original implementation
  318. // with our new implementation and save the old implementation.
  319. IMP __original_method_implementation =
  320. method_setImplementation(originalMethod, swizzledImplementation);
  321. IMP __nonexistant_method_implementation = [self nonExistantMethodImplementationForClass:klass];
  322. if (__original_method_implementation &&
  323. __original_method_implementation != __nonexistant_method_implementation &&
  324. __original_method_implementation != swizzledImplementation) {
  325. [self saveOriginalImplementation:__original_method_implementation
  326. forSelector:originalSelector];
  327. }
  328. } else {
  329. // The class doesn't have this method, so add our swizzled implementation as the
  330. // original implementation of the original method.
  331. struct objc_method_description method_description =
  332. protocol_getMethodDescription(protocol, originalSelector, NO, YES);
  333. BOOL methodAdded = class_addMethod(klass,
  334. originalSelector,
  335. swizzledImplementation,
  336. method_description.types);
  337. if (!methodAdded) {
  338. FIRMessagingLoggerError(kFIRMessagingMessageCodeRemoteNotificationsProxyMethodNotAdded,
  339. @"Could not add method for %@ to class %@",
  340. NSStringFromSelector(originalSelector),
  341. NSStringFromClass(klass));
  342. }
  343. }
  344. [self trackSwizzledSelector:originalSelector ofClass:klass];
  345. }
  346. - (void)unswizzleSelector:(SEL)selector inClass:(Class)klass {
  347. Method swizzledMethod = class_getInstanceMethod(klass, selector);
  348. if (!swizzledMethod) {
  349. // This class doesn't seem to have this selector as an instance method? Bail out.
  350. return;
  351. }
  352. IMP original_imp = [self originalImplementationForSelector:selector];
  353. if (original_imp) {
  354. // Restore the original implementation as the current implementation
  355. method_setImplementation(swizzledMethod, original_imp);
  356. [self removeImplementationForSelector:selector];
  357. } else {
  358. // This class originally did not have an implementation for this selector.
  359. // We can't actually remove methods in Objective C 2.0, but we could set
  360. // its method to something non-existent. This should give us the same
  361. // behavior as if the method was not implemented.
  362. // See: http://stackoverflow.com/a/8276527/9849
  363. IMP nonExistantMethodImplementation = [self nonExistantMethodImplementationForClass:klass];
  364. method_setImplementation(swizzledMethod, nonExistantMethodImplementation);
  365. }
  366. }
  367. #pragma mark - Reflection Helpers
  368. // This is useful to generate from a stable, "known missing" selector, as the IMP can be compared
  369. // in case we are setting an implementation for a class that was previously "unswizzled" into a
  370. // non-existant implementation.
  371. - (IMP)nonExistantMethodImplementationForClass:(Class)klass {
  372. SEL nonExistantSelector = NSSelectorFromString(@"aNonExistantMethod");
  373. IMP nonExistantMethodImplementation = class_getMethodImplementation(klass, nonExistantSelector);
  374. return nonExistantMethodImplementation;
  375. }
  376. // A safe, non-leaky way return a property object by its name
  377. id getNamedPropertyFromObject(id object, NSString *propertyName, Class klass) {
  378. SEL selector = NSSelectorFromString(propertyName);
  379. if (![object respondsToSelector:selector]) {
  380. return nil;
  381. }
  382. if (!klass) {
  383. klass = [NSObject class];
  384. }
  385. // Suppress clang warning about leaks in performSelector
  386. // The alternative way to perform this is to invoke
  387. // the method as a block (see http://stackoverflow.com/a/20058585),
  388. // but this approach sometimes returns incomplete objects.
  389. #pragma clang diagnostic push
  390. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  391. id property = [object performSelector:selector];
  392. #pragma clang diagnostic pop
  393. if (![property isKindOfClass:klass]) {
  394. return nil;
  395. }
  396. return property;
  397. }
  398. #pragma mark - Swizzled Methods
  399. void FCM_swizzle_appDidReceiveRemoteNotification(id self,
  400. SEL _cmd,
  401. UIApplication *app,
  402. NSDictionary *userInfo) {
  403. [[FIRMessaging messaging] appDidReceiveMessage:userInfo];
  404. IMP original_imp =
  405. [[FIRMessagingRemoteNotificationsProxy sharedProxy] originalImplementationForSelector:_cmd];
  406. if (original_imp) {
  407. ((void (*)(id, SEL, UIApplication *, NSDictionary *))original_imp)(self,
  408. _cmd,
  409. app,
  410. userInfo);
  411. }
  412. }
  413. void FCM_swizzle_appDidReceiveRemoteNotificationWithHandler(
  414. id self, SEL _cmd, UIApplication *app, NSDictionary *userInfo,
  415. void (^handler)(UIBackgroundFetchResult)) {
  416. [[FIRMessaging messaging] appDidReceiveMessage:userInfo];
  417. IMP original_imp =
  418. [[FIRMessagingRemoteNotificationsProxy sharedProxy] originalImplementationForSelector:_cmd];
  419. if (original_imp) {
  420. ((void (*)(id, SEL, UIApplication *, NSDictionary *,
  421. void (^)(UIBackgroundFetchResult)))original_imp)(
  422. self, _cmd, app, userInfo, handler);
  423. }
  424. }
  425. /**
  426. * Swizzle the notification handler for iOS 10+ devices.
  427. * Signature of original handler is as below:
  428. * - (void)userNotificationCenter:(UNUserNotificationCenter *)center
  429. * willPresentNotification:(UNNotification *)notification
  430. * withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
  431. * In order to make FCM SDK compile and compatible with iOS SDKs before iOS 10, hide the
  432. * parameter types from the swizzling implementation.
  433. */
  434. void FCM_swizzle_willPresentNotificationWithHandler(
  435. id self, SEL _cmd, id center, id notification, void (^handler)(NSUInteger)) {
  436. FIRMessagingRemoteNotificationsProxy *proxy = [FIRMessagingRemoteNotificationsProxy sharedProxy];
  437. IMP original_imp = [proxy originalImplementationForSelector:_cmd];
  438. void (^callOriginalMethodIfAvailable)(void) = ^{
  439. if (original_imp) {
  440. ((void (*)(id, SEL, id, id, void (^)(NSUInteger)))original_imp)(
  441. self, _cmd, center, notification, handler);
  442. }
  443. return;
  444. };
  445. Class notificationCenterClass = NSClassFromString(@"UNUserNotificationCenter");
  446. Class notificationClass = NSClassFromString(@"UNNotification");
  447. if (!notificationCenterClass || !notificationClass) {
  448. // Can't find UserNotifications framework. Do not swizzle, just execute the original method.
  449. callOriginalMethodIfAvailable();
  450. }
  451. if (!center || ![center isKindOfClass:[notificationCenterClass class]]) {
  452. // Invalid parameter type from the original method.
  453. // Do not swizzle, just execute the original method.
  454. callOriginalMethodIfAvailable();
  455. return;
  456. }
  457. if (!notification || ![notification isKindOfClass:[notificationClass class]]) {
  458. // Invalid parameter type from the original method.
  459. // Do not swizzle, just execute the original method.
  460. callOriginalMethodIfAvailable();
  461. return;
  462. }
  463. if (!handler) {
  464. // Invalid parameter type from the original method.
  465. // Do not swizzle, just execute the original method.
  466. callOriginalMethodIfAvailable();
  467. return;
  468. }
  469. // Attempt to access the user info
  470. id notificationUserInfo = userInfoFromNotification(notification);
  471. if (!notificationUserInfo) {
  472. // Could not access notification.request.content.userInfo.
  473. callOriginalMethodIfAvailable();
  474. return;
  475. }
  476. [[FIRMessaging messaging] appDidReceiveMessage:notificationUserInfo];
  477. // Execute the original implementation.
  478. callOriginalMethodIfAvailable();
  479. }
  480. /**
  481. * Swizzle the notification handler for iOS 10+ devices.
  482. * Signature of original handler is as below:
  483. * - (void)userNotificationCenter:(UNUserNotificationCenter *)center
  484. * didReceiveNotificationResponse:(UNNotificationResponse *)response
  485. * withCompletionHandler:(void (^)(void))completionHandler
  486. * In order to make FCM SDK compile and compatible with iOS SDKs before iOS 10, hide the
  487. * parameter types from the swizzling implementation.
  488. */
  489. void FCM_swizzle_didReceiveNotificationResponseWithHandler(
  490. id self, SEL _cmd, id center, id response, void (^handler)(void)) {
  491. FIRMessagingRemoteNotificationsProxy *proxy = [FIRMessagingRemoteNotificationsProxy sharedProxy];
  492. IMP original_imp = [proxy originalImplementationForSelector:_cmd];
  493. void (^callOriginalMethodIfAvailable)(void) = ^{
  494. if (original_imp) {
  495. ((void (*)(id, SEL, id, id, void (^)(void)))original_imp)(
  496. self, _cmd, center, response, handler);
  497. }
  498. return;
  499. };
  500. Class notificationCenterClass = NSClassFromString(@"UNUserNotificationCenter");
  501. Class responseClass = NSClassFromString(@"UNNotificationResponse");
  502. if (!center || ![center isKindOfClass:[notificationCenterClass class]]) {
  503. // Invalid parameter type from the original method.
  504. // Do not swizzle, just execute the original method.
  505. callOriginalMethodIfAvailable();
  506. return;
  507. }
  508. if (!response || ![response isKindOfClass:[responseClass class]]) {
  509. // Invalid parameter type from the original method.
  510. // Do not swizzle, just execute the original method.
  511. callOriginalMethodIfAvailable();
  512. return;
  513. }
  514. if (!handler) {
  515. // Invalid parameter type from the original method.
  516. // Do not swizzle, just execute the original method.
  517. callOriginalMethodIfAvailable();
  518. return;
  519. }
  520. // Try to access the response.notification property
  521. SEL notificationSelector = NSSelectorFromString(@"notification");
  522. if (![response respondsToSelector:notificationSelector]) {
  523. // Cannot access the .notification property.
  524. callOriginalMethodIfAvailable();
  525. return;
  526. }
  527. id notificationClass = NSClassFromString(@"UNNotification");
  528. id notification = getNamedPropertyFromObject(response, @"notification", notificationClass);
  529. // With a notification object, use the common code to reach deep into notification
  530. // (notification.request.content.userInfo)
  531. id notificationUserInfo = userInfoFromNotification(notification);
  532. if (!notificationUserInfo) {
  533. // Could not access notification.request.content.userInfo.
  534. callOriginalMethodIfAvailable();
  535. return;
  536. }
  537. [[FIRMessaging messaging] appDidReceiveMessage:notificationUserInfo];
  538. // Execute the original implementation.
  539. callOriginalMethodIfAvailable();
  540. }
  541. id userInfoFromNotification(id notification) {
  542. // Select the userInfo field from UNNotification.request.content.userInfo.
  543. SEL requestSelector = NSSelectorFromString(@"request");
  544. if (![notification respondsToSelector:requestSelector]) {
  545. // Cannot access the request property.
  546. return nil;
  547. }
  548. Class requestClass = NSClassFromString(@"UNNotificationRequest");
  549. id notificationRequest = getNamedPropertyFromObject(notification, @"request", requestClass);
  550. SEL notificationContentSelector = NSSelectorFromString(@"content");
  551. if (!notificationRequest
  552. || ![notificationRequest respondsToSelector:notificationContentSelector]) {
  553. // Cannot access the content property.
  554. return nil;
  555. }
  556. Class contentClass = NSClassFromString(@"UNNotificationContent");
  557. id notificationContent = getNamedPropertyFromObject(notificationRequest,
  558. @"content",
  559. contentClass);
  560. SEL notificationUserInfoSelector = NSSelectorFromString(@"userInfo");
  561. if (!notificationContent
  562. || ![notificationContent respondsToSelector:notificationUserInfoSelector]) {
  563. // Cannot access the userInfo property.
  564. return nil;
  565. }
  566. id notificationUserInfo = getNamedPropertyFromObject(notificationContent,
  567. @"userInfo",
  568. [NSDictionary class]);
  569. if (!notificationUserInfo) {
  570. // This is not the expected notification handler.
  571. return nil;
  572. }
  573. return notificationUserInfo;
  574. }
  575. void FCM_swizzle_applicationReceivedRemoteMessage(
  576. id self, SEL _cmd, FIRMessagingRemoteMessage *remoteMessage) {
  577. [[FIRMessaging messaging] appDidReceiveMessage:remoteMessage.appData];
  578. IMP original_imp =
  579. [[FIRMessagingRemoteNotificationsProxy sharedProxy] originalImplementationForSelector:_cmd];
  580. if (original_imp) {
  581. ((void (*)(id, SEL, FIRMessagingRemoteMessage *))original_imp)(self, _cmd, remoteMessage);
  582. }
  583. }
  584. void FCM_swizzle_appDidFailToRegisterForRemoteNotifications(id self,
  585. SEL _cmd,
  586. UIApplication *app,
  587. NSError *error) {
  588. // Log the fact that we failed to register for remote notifications
  589. FIRMessagingLoggerError(kFIRMessagingMessageCodeRemoteNotificationsProxyAPNSFailed,
  590. @"Error in "
  591. @"application:didFailToRegisterForRemoteNotificationsWithError: %@",
  592. error.localizedDescription);
  593. IMP original_imp =
  594. [[FIRMessagingRemoteNotificationsProxy sharedProxy] originalImplementationForSelector:_cmd];
  595. if (original_imp) {
  596. ((void (*)(id, SEL, UIApplication *, NSError *))original_imp)(self, _cmd, app, error);
  597. }
  598. }
  599. void FCM_swizzle_appDidRegisterForRemoteNotifications(id self,
  600. SEL _cmd,
  601. UIApplication *app,
  602. NSData *deviceToken) {
  603. // Pass the APNSToken along to FIRMessaging (and auto-detect the token type)
  604. [FIRMessaging messaging].APNSToken = deviceToken;
  605. IMP original_imp =
  606. [[FIRMessagingRemoteNotificationsProxy sharedProxy] originalImplementationForSelector:_cmd];
  607. if (original_imp) {
  608. ((void (*)(id, SEL, UIApplication *, NSData *))original_imp)(self, _cmd, app, deviceToken);
  609. }
  610. }
  611. @end