FIRMessagingRemoteNotificationsProxy.m 29 KB

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