FIRMessagingRemoteNotificationsProxy.m 24 KB

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