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