Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

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