FIRMessagingRemoteNotificationsProxy.m 24 KB

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