You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

602 lines
24 KiB

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