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.

714 lines
28 KiB

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