Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
 
2
/*
3
 * Copyright 2017 Google
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
 
18
#if !__has_feature(objc_arc)
19
#error FIRMessagingLib should be compiled with ARC.
20
#endif
21
 
22
#import "FirebaseMessaging/Sources/Public/FirebaseMessaging/FIRMessaging.h"
23
#import <GoogleUtilities/GULAppDelegateSwizzler.h>
24
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
25
#import <GoogleUtilities/GULReachabilityChecker.h>
26
#import <GoogleUtilities/GULUserDefaults.h>
27
#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
28
#import "FirebaseInstallations/Source/Library/Private/FirebaseInstallationsInternal.h"
29
#import "FirebaseMessaging/Sources/FIRMessagingAnalytics.h"
30
#import "FirebaseMessaging/Sources/FIRMessagingCode.h"
31
#import "FirebaseMessaging/Sources/FIRMessagingConstants.h"
32
#import "FirebaseMessaging/Sources/FIRMessagingContextManagerService.h"
33
#import "FirebaseMessaging/Sources/FIRMessagingDefines.h"
34
#import "FirebaseMessaging/Sources/FIRMessagingLogger.h"
35
#import "FirebaseMessaging/Sources/FIRMessagingPubSub.h"
36
#import "FirebaseMessaging/Sources/FIRMessagingRemoteNotificationsProxy.h"
37
#import "FirebaseMessaging/Sources/FIRMessagingRmqManager.h"
38
#import "FirebaseMessaging/Sources/FIRMessagingSyncMessageManager.h"
39
#import "FirebaseMessaging/Sources/FIRMessagingUtilities.h"
40
#import "FirebaseMessaging/Sources/FIRMessaging_Private.h"
41
#import "FirebaseMessaging/Sources/Interop/FIRMessagingInterop.h"
42
#import "FirebaseMessaging/Sources/NSError+FIRMessaging.h"
43
#import "FirebaseMessaging/Sources/Public/FirebaseMessaging/FIRMessagingExtensionHelper.h"
44
#import "FirebaseMessaging/Sources/Token/FIRMessagingAuthService.h"
45
#import "FirebaseMessaging/Sources/Token/FIRMessagingTokenInfo.h"
46
#import "FirebaseMessaging/Sources/Token/FIRMessagingTokenManager.h"
47
#import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
48
 
49
static NSString *const kFIRMessagingMessageViaAPNSRootKey = @"aps";
50
static NSString *const kFIRMessagingReachabilityHostname = @"www.google.com";
51
 
52
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
53
const NSNotificationName FIRMessagingRegistrationTokenRefreshedNotification =
54
    @"com.firebase.messaging.notif.fcm-token-refreshed";
55
#else
56
NSString *const FIRMessagingRegistrationTokenRefreshedNotification =
57
    @"com.firebase.messaging.notif.fcm-token-refreshed";
58
#endif  // defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
59
 
60
NSString *const kFIRMessagingUserDefaultsKeyAutoInitEnabled =
61
    @"com.firebase.messaging.auto-init.enabled";  // Auto Init Enabled key stored in NSUserDefaults
62
 
63
NSString *const kFIRMessagingPlistAutoInitEnabled =
64
    @"FirebaseMessagingAutoInitEnabled";  // Auto Init Enabled key stored in Info.plist
65
 
66
const BOOL FIRMessagingIsAPNSSyncMessage(NSDictionary *message) {
67
  if ([message[kFIRMessagingMessageViaAPNSRootKey] isKindOfClass:[NSDictionary class]]) {
68
    NSDictionary *aps = message[kFIRMessagingMessageViaAPNSRootKey];
69
    if (aps && [aps isKindOfClass:[NSDictionary class]]) {
70
      return [aps[kFIRMessagingMessageAPNSContentAvailableKey] boolValue];
71
    }
72
  }
73
  return NO;
74
}
75
 
76
BOOL FIRMessagingIsContextManagerMessage(NSDictionary *message) {
77
  return [FIRMessagingContextManagerService isContextManagerMessage:message];
78
}
79
 
80
@interface FIRMessagingMessageInfo ()
81
 
82
@property(nonatomic, readwrite, assign) FIRMessagingMessageStatus status;
83
 
84
@end
85
 
86
@implementation FIRMessagingMessageInfo
87
 
88
- (instancetype)init {
89
  FIRMessagingInvalidateInitializer();
90
}
91
 
92
- (instancetype)initWithStatus:(FIRMessagingMessageStatus)status {
93
  self = [super init];
94
  if (self) {
95
    _status = status;
96
  }
97
  return self;
98
}
99
 
100
@end
101
 
102
@interface FIRMessaging () <GULReachabilityDelegate>
103
 
104
// FIRApp properties
105
@property(nonatomic, readwrite, strong) NSData *apnsTokenData;
106
@property(nonatomic, readwrite, strong) FIRMessagingClient *client;
107
@property(nonatomic, readwrite, strong) GULReachabilityChecker *reachability;
108
@property(nonatomic, readwrite, strong) FIRMessagingPubSub *pubsub;
109
@property(nonatomic, readwrite, strong) FIRMessagingRmqManager *rmq2Manager;
110
@property(nonatomic, readwrite, strong) FIRMessagingSyncMessageManager *syncMessageManager;
111
@property(nonatomic, readwrite, strong) GULUserDefaults *messagingUserDefaults;
112
@property(nonatomic, readwrite, strong) FIRInstallations *installations;
113
@property(nonatomic, readwrite, strong) FIRMessagingTokenManager *tokenManager;
114
 
115
/// Message ID's logged for analytics. This prevents us from logging the same message twice
116
/// which can happen if the user inadvertently calls `appDidReceiveMessage` along with us
117
/// calling it implicitly during swizzling.
118
@property(nonatomic, readwrite, strong) NSMutableSet *loggedMessageIDs;
119
@property(nonatomic, readwrite, strong) id<FIRAnalyticsInterop> _Nullable analytics;
120
 
121
@end
122
 
123
@interface FIRMessaging () <FIRMessagingInterop, FIRLibrary>
124
@end
125
 
126
@implementation FIRMessaging
127
 
128
+ (FIRMessaging *)messaging {
129
  FIRApp *defaultApp = [FIRApp defaultApp];  // Missing configure will be logged here.
130
  id<FIRMessagingInterop> instance = FIR_COMPONENT(FIRMessagingInterop, defaultApp.container);
131
 
132
  // We know the instance coming from the container is a FIRMessaging instance, cast it and move on.
133
  return (FIRMessaging *)instance;
134
}
135
 
136
+ (FIRMessagingExtensionHelper *)extensionHelper {
137
  static dispatch_once_t once;
138
  static FIRMessagingExtensionHelper *extensionHelper;
139
  dispatch_once(&once, ^{
140
    extensionHelper = [[FIRMessagingExtensionHelper alloc] init];
141
  });
142
  return extensionHelper;
143
}
144
#pragma clang diagnostic push
145
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
146
- (instancetype)initWithAnalytics:(nullable id<FIRAnalyticsInterop>)analytics
147
                 withUserDefaults:(GULUserDefaults *)defaults {
148
#pragma clang diagnostic pop
149
  self = [super init];
150
  if (self != nil) {
151
    _loggedMessageIDs = [NSMutableSet set];
152
    _messagingUserDefaults = defaults;
153
    _analytics = analytics;
154
  }
155
  return self;
156
}
157
 
158
- (void)dealloc {
159
  [self.reachability stop];
160
  [[NSNotificationCenter defaultCenter] removeObserver:self];
161
  [self teardown];
162
}
163
 
164
#pragma mark - Config
165
 
166
+ (void)load {
167
  [FIRApp registerInternalLibrary:(Class<FIRLibrary>)self withName:@"fire-fcm"];
168
}
169
 
170
+ (nonnull NSArray<FIRComponent *> *)componentsToRegister {
171
  FIRDependency *analyticsDep = [FIRDependency dependencyWithProtocol:@protocol(FIRAnalyticsInterop)
172
                                                           isRequired:NO];
173
  FIRComponentCreationBlock creationBlock =
174
      ^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) {
175
    if (!container.app.isDefaultApp) {
176
      // Only start for the default FIRApp.
177
      FIRMessagingLoggerDebug(kFIRMessagingMessageCodeFIRApp001,
178
                              @"Firebase Messaging only works with the default app.");
179
      return nil;
180
    }
181
 
182
    // Ensure it's cached so it returns the same instance every time messaging is called.
183
    *isCacheable = YES;
184
    id<FIRAnalyticsInterop> analytics = FIR_COMPONENT(FIRAnalyticsInterop, container);
185
#pragma clang diagnostic push
186
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
187
    FIRMessaging *messaging =
188
        [[FIRMessaging alloc] initWithAnalytics:analytics
189
                               withUserDefaults:[GULUserDefaults standardUserDefaults]];
190
#pragma clang diagnostic pop
191
    [messaging start];
192
    [messaging configureMessagingWithOptions:container.app.options];
193
 
194
    [messaging configureNotificationSwizzlingIfEnabled];
195
    return messaging;
196
  };
197
  FIRComponent *messagingProvider =
198
      [FIRComponent componentWithProtocol:@protocol(FIRMessagingInterop)
199
                      instantiationTiming:FIRInstantiationTimingEagerInDefaultApp
200
                             dependencies:@[ analyticsDep ]
201
                            creationBlock:creationBlock];
202
 
203
  return @[ messagingProvider ];
204
}
205
 
206
- (void)configureMessagingWithOptions:(FIROptions *)options {
207
  NSString *GCMSenderID = options.GCMSenderID;
208
  if (!GCMSenderID.length) {
209
    FIRMessagingLoggerError(kFIRMessagingMessageCodeFIRApp000,
210
                            @"Firebase not set up correctly, nil or empty senderID.");
211
    [NSException raise:kFIRMessagingDomain
212
                format:@"Could not configure Firebase Messaging. GCMSenderID must not be nil or "
213
                       @"empty."];
214
  }
215
 
216
  self.tokenManager.fcmSenderID = GCMSenderID;
217
  self.tokenManager.firebaseAppID = options.googleAppID;
218
 
219
  // FCM generates a FCM token during app start for sending push notification to device.
220
  // This is not needed for app extension except for watch.
221
#if TARGET_OS_WATCH
222
  [self didCompleteConfigure];
223
#else
224
  if (![GULAppEnvironmentUtil isAppExtension]) {
225
    [self didCompleteConfigure];
226
  }
227
#endif
228
}
229
 
230
- (void)didCompleteConfigure {
231
  NSString *cachedToken =
232
      [self.tokenManager cachedTokenInfoWithAuthorizedEntity:self.tokenManager.fcmSenderID
233
                                                       scope:kFIRMessagingDefaultTokenScope]
234
          .token;
235
  // When there is a cached token, do the token refresh.
236
  if (cachedToken) {
237
    // Clean up expired tokens by checking the token refresh policy.
238
    [self.installations installationIDWithCompletion:^(NSString *_Nullable identifier,
239
                                                       NSError *_Nullable error) {
240
      if ([self.tokenManager checkTokenRefreshPolicyWithIID:identifier]) {
241
        // Default token is expired, fetch default token from server.
242
        [self retrieveFCMTokenForSenderID:self.tokenManager.fcmSenderID
243
                               completion:^(NSString *_Nullable FCMToken, NSError *_Nullable error){
244
                               }];
245
      }
246
      // Set the default FCM token, there's an issue that FIRApp configure
247
      // happens before developers able to set the delegate
248
      // Hence first token set must be happen here after listener is set
249
      // TODO(chliangGoogle) Need to investigate better solution.
250
      [self updateDefaultFCMToken:self.FCMToken];
251
    }];
252
  } else if (self.isAutoInitEnabled) {
253
    // When there is no cached token, must check auto init is enabled.
254
    // If it's disabled, don't initiate token generation/refresh.
255
    // If no cache token and auto init is enabled, fetch a token from server.
256
    [self retrieveFCMTokenForSenderID:self.tokenManager.fcmSenderID
257
                           completion:^(NSString *_Nullable FCMToken, NSError *_Nullable error){
258
                           }];
259
  }
260
}
261
 
262
- (void)configureNotificationSwizzlingIfEnabled {
263
  // Swizzle remote-notification-related methods (app delegate and UNUserNotificationCenter)
264
  if ([FIRMessagingRemoteNotificationsProxy canSwizzleMethods]) {
265
    NSString *docsURLString = @"https://firebase.google.com/docs/cloud-messaging/ios/client"
266
                              @"#method_swizzling_in_firebase_messaging";
267
    FIRMessagingLoggerNotice(kFIRMessagingMessageCodeFIRApp000,
268
                             @"FIRMessaging Remote Notifications proxy enabled, will swizzle "
269
                             @"remote notification receiver handlers. If you'd prefer to manually "
270
                             @"integrate Firebase Messaging, add \"%@\" to your Info.plist, "
271
                             @"and set it to NO. Follow the instructions at:\n%@\nto ensure "
272
                             @"proper integration.",
273
                             kFIRMessagingRemoteNotificationsProxyEnabledInfoPlistKey,
274
                             docsURLString);
275
    [[FIRMessagingRemoteNotificationsProxy sharedProxy] swizzleMethodsIfPossible];
276
  }
277
}
278
 
279
- (void)start {
280
  [self setupFileManagerSubDirectory];
281
  [self setupNotificationListeners];
282
 
283
  self.tokenManager = [[FIRMessagingTokenManager alloc] init];
284
  self.installations = [FIRInstallations installations];
285
  [self setupTopics];
286
 
287
  // Print the library version for logging.
288
  NSString *currentLibraryVersion = FIRFirebaseVersion();
289
  FIRMessagingLoggerInfo(kFIRMessagingMessageCodeMessagingPrintLibraryVersion,
290
                         @"FIRMessaging library version %@", currentLibraryVersion);
291
 
292
  NSString *hostname = kFIRMessagingReachabilityHostname;
293
  self.reachability = [[GULReachabilityChecker alloc] initWithReachabilityDelegate:self
294
                                                                          withHost:hostname];
295
  [self.reachability start];
296
 
297
  // setup FIRMessaging objects
298
  [self setupRmqManager];
299
  [self setupSyncMessageManager];
300
}
301
 
302
- (void)setupFileManagerSubDirectory {
303
  if (![[self class] hasSubDirectory:kFIRMessagingSubDirectoryName]) {
304
    [[self class] createSubDirectory:kFIRMessagingSubDirectoryName];
305
  }
306
  if (![[self class] hasSubDirectory:kFIRMessagingInstanceIDSubDirectoryName]) {
307
    [[self class] createSubDirectory:kFIRMessagingInstanceIDSubDirectoryName];
308
  }
309
}
310
 
311
- (void)setupNotificationListeners {
312
  // To prevent multiple notifications remove self as observer for all events.
313
  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
314
  [center removeObserver:self];
315
  [center addObserver:self
316
             selector:@selector(defaultFCMTokenWasRefreshed:)
317
                 name:kFIRMessagingRegistrationTokenRefreshNotification
318
               object:nil];
319
}
320
 
321
- (void)setupRmqManager {
322
  self.rmq2Manager = [[FIRMessagingRmqManager alloc] initWithDatabaseName:@"rmq2"];
323
  [self.rmq2Manager loadRmqId];
324
}
325
 
326
- (void)setupTopics {
327
  self.pubsub = [[FIRMessagingPubSub alloc] initWithTokenManager:self.tokenManager];
328
}
329
 
330
- (void)setupSyncMessageManager {
331
  self.syncMessageManager =
332
      [[FIRMessagingSyncMessageManager alloc] initWithRmqManager:self.rmq2Manager];
333
  [self.syncMessageManager removeExpiredSyncMessages];
334
}
335
 
336
- (void)teardown {
337
  self.pubsub = nil;
338
  self.syncMessageManager = nil;
339
  self.rmq2Manager = nil;
340
  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeMessaging001, @"Did successfully teardown");
341
}
342
 
343
#pragma mark - Messages
344
 
345
- (FIRMessagingMessageInfo *)appDidReceiveMessage:(NSDictionary *)message {
346
  if (!message.count) {
347
    return [[FIRMessagingMessageInfo alloc] initWithStatus:FIRMessagingMessageStatusUnknown];
348
  }
349
 
350
  // For downstream messages that go via MCS we should strip out this key before sending
351
  // the message to the device.
352
  BOOL isOldMessage = NO;
353
  NSString *messageID = message[kFIRMessagingMessageIDKey];
354
  if (messageID.length) {
355
    [self.rmq2Manager saveS2dMessageWithRmqId:messageID];
356
 
357
    BOOL isSyncMessage = FIRMessagingIsAPNSSyncMessage(message);
358
    if (isSyncMessage) {
359
      isOldMessage = [self.syncMessageManager didReceiveAPNSSyncMessage:message];
360
    }
361
 
362
    // Prevent duplicates by keeping a cache of all the logged messages during each session.
363
    // The duplicates only happen when the 3P app calls `appDidReceiveMessage:` along with
364
    // us swizzling their implementation to call the same method implicitly.
365
    // We need to rule out the contextual message because it shares the same message ID
366
    // as the local notification it will schedule. And because it is also a APNSSync message
367
    // its duplication is already checked previously.
368
    if (!isOldMessage && !FIRMessagingIsContextManagerMessage(message)) {
369
      isOldMessage = [self.loggedMessageIDs containsObject:messageID];
370
      if (!isOldMessage) {
371
        [self.loggedMessageIDs addObject:messageID];
372
      }
373
    }
374
  }
375
 
376
  if (!isOldMessage) {
377
    [FIRMessagingAnalytics logMessage:message toAnalytics:_analytics];
378
    [self handleContextManagerMessage:message];
379
    [self handleIncomingLinkIfNeededFromMessage:message];
380
  }
381
  return [[FIRMessagingMessageInfo alloc] initWithStatus:FIRMessagingMessageStatusNew];
382
}
383
 
384
- (BOOL)handleContextManagerMessage:(NSDictionary *)message {
385
  if (FIRMessagingIsContextManagerMessage(message)) {
386
    return [FIRMessagingContextManagerService handleContextManagerMessage:message];
387
  }
388
  return NO;
389
}
390
 
391
- (void)handleIncomingLinkIfNeededFromMessage:(NSDictionary *)message {
392
#if TARGET_OS_IOS || TARGET_OS_TV
393
  NSURL *url = [self linkURLFromMessage:message];
394
  if (url == nil) {
395
    return;
396
  }
397
  if (![NSThread isMainThread]) {
398
    dispatch_async(dispatch_get_main_queue(), ^{
399
      [self handleIncomingLinkIfNeededFromMessage:message];
400
    });
401
    return;
402
  }
403
  UIApplication *application = [GULAppDelegateSwizzler sharedApplication];
404
  if (!application) {
405
    return;
406
  }
407
  id<UIApplicationDelegate> appDelegate = application.delegate;
408
  SEL continueUserActivitySelector = @selector(application:
409
                                      continueUserActivity:restorationHandler:);
410
 
411
  SEL openURLWithOptionsSelector = @selector(application:openURL:options:);
412
  SEL openURLWithSourceApplicationSelector = @selector(application:
413
                                                           openURL:sourceApplication:annotation:);
414
#if TARGET_OS_IOS
415
  SEL handleOpenURLSelector = @selector(application:handleOpenURL:);
416
#endif
417
  // Due to FIRAAppDelegateProxy swizzling, this selector will most likely get chosen, whether or
418
  // not the actual application has implemented
419
  // |application:continueUserActivity:restorationHandler:|. A warning will be displayed to the user
420
  // if they haven't implemented it.
421
  if ([NSUserActivity class] != nil &&
422
      [appDelegate respondsToSelector:continueUserActivitySelector]) {
423
    NSUserActivity *userActivity =
424
        [[NSUserActivity alloc] initWithActivityType:NSUserActivityTypeBrowsingWeb];
425
    userActivity.webpageURL = url;
426
    [appDelegate application:application
427
        continueUserActivity:userActivity
428
          restorationHandler:^(NSArray *_Nullable restorableObjects){
429
              // Do nothing, as we don't support the app calling this block
430
          }];
431
 
432
  } else if ([appDelegate respondsToSelector:openURLWithOptionsSelector]) {
433
#pragma clang diagnostic push
434
#pragma clang diagnostic ignored "-Wunguarded-availability"
435
    [appDelegate application:application openURL:url options:@{}];
436
#pragma clang diagnostic pop
437
    // Similarly, |application:openURL:sourceApplication:annotation:| will also always be called,
438
    // due to the default swizzling done by FIRAAppDelegateProxy in Firebase Analytics
439
  } else if ([appDelegate respondsToSelector:openURLWithSourceApplicationSelector]) {
440
#if TARGET_OS_IOS
441
#pragma clang diagnostic push
442
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
443
    [appDelegate application:application
444
                     openURL:url
445
           sourceApplication:FIRMessagingAppIdentifier()
446
                  annotation:@{}];
447
#pragma clang diagnostic pop
448
  } else if ([appDelegate respondsToSelector:handleOpenURLSelector]) {
449
#pragma clang diagnostic push
450
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
451
    [appDelegate application:application handleOpenURL:url];
452
#pragma clang diagnostic pop
453
#endif
454
  }
455
#endif
456
}
457
 
458
- (NSURL *)linkURLFromMessage:(NSDictionary *)message {
459
  NSString *urlString = message[kFIRMessagingMessageLinkKey];
460
  if (urlString == nil || ![urlString isKindOfClass:[NSString class]] || urlString.length == 0) {
461
    return nil;
462
  }
463
  NSURL *url = [NSURL URLWithString:urlString];
464
  return url;
465
}
466
 
467
#pragma mark - APNS
468
 
469
- (NSData *)APNSToken {
470
  return self.apnsTokenData;
471
}
472
 
473
- (void)setAPNSToken:(NSData *)APNSToken {
474
  [self setAPNSToken:APNSToken type:FIRMessagingAPNSTokenTypeUnknown];
475
}
476
 
477
- (void)setAPNSToken:(NSData *)apnsToken type:(FIRMessagingAPNSTokenType)type {
478
  if ([apnsToken isEqual:self.apnsTokenData]) {
479
    return;
480
  }
481
  self.apnsTokenData = apnsToken;
482
 
483
  // Notify InstanceID that APNS Token has been set.
484
  NSDictionary *userInfo = @{kFIRMessagingAPNSTokenType : @(type)};
485
  // TODO(chliang) This is sent to InstanceID in case users are still using the deprecated SDK.
486
  // Should be safe to remove once InstanceID is removed.
487
  NSNotification *notification =
488
      [NSNotification notificationWithName:kFIRMessagingAPNSTokenNotification
489
                                    object:[apnsToken copy]
490
                                  userInfo:userInfo];
491
  [[NSNotificationQueue defaultQueue] enqueueNotification:notification postingStyle:NSPostASAP];
492
 
493
  [self.tokenManager setAPNSToken:[apnsToken copy] withUserInfo:userInfo];
494
}
495
 
496
#pragma mark - FCM Token
497
 
498
- (BOOL)isAutoInitEnabled {
499
  // Defer to the class method since we're just reading from regular userDefaults and we need to
500
  // read this from IID without instantiating the Messaging singleton.
501
  return [[self class] isAutoInitEnabledWithUserDefaults:_messagingUserDefaults];
502
}
503
 
504
/// Checks if Messaging auto-init is enabled in the user defaults instance passed in. This is
505
/// exposed as a class property for IID to fetch the property without instantiating an instance of
506
/// Messaging. Since Messaging can only be used with the default FIRApp, we can have one point of
507
/// entry without context of which FIRApp instance is being used.
508
/// ** THIS METHOD IS DEPENDED ON INTERNALLY BY IID USING REFLECTION. PLEASE DO NOT CHANGE THE
509
///  SIGNATURE, AS IT WOULD BREAK AUTOINIT FUNCTIONALITY WITHIN IID. **
510
+ (BOOL)isAutoInitEnabledWithUserDefaults:(GULUserDefaults *)userDefaults {
511
  // Check storage
512
  id isAutoInitEnabledObject =
513
      [userDefaults objectForKey:kFIRMessagingUserDefaultsKeyAutoInitEnabled];
514
  if (isAutoInitEnabledObject) {
515
    return [isAutoInitEnabledObject boolValue];
516
  }
517
 
518
  // Check Info.plist
519
  isAutoInitEnabledObject =
520
      [[NSBundle mainBundle] objectForInfoDictionaryKey:kFIRMessagingPlistAutoInitEnabled];
521
  if (isAutoInitEnabledObject) {
522
    return [isAutoInitEnabledObject boolValue];
523
  }
524
 
525
  // If none of above exists, we default to the global switch that comes from FIRApp.
526
  return [[FIRApp defaultApp] isDataCollectionDefaultEnabled];
527
}
528
 
529
- (void)setAutoInitEnabled:(BOOL)autoInitEnabled {
530
  BOOL isFCMAutoInitEnabled = [self isAutoInitEnabled];
531
  [_messagingUserDefaults setBool:autoInitEnabled
532
                           forKey:kFIRMessagingUserDefaultsKeyAutoInitEnabled];
533
  [_messagingUserDefaults synchronize];
534
  if (!isFCMAutoInitEnabled && autoInitEnabled) {
535
    [self.tokenManager tokenAndRequestIfNotExist];
536
  }
537
}
538
 
539
- (NSString *)FCMToken {
540
  // Gets the current default token, and requets a new one if it doesn't exist.
541
  NSString *token = [self.tokenManager tokenAndRequestIfNotExist];
542
  return token;
543
}
544
 
545
- (void)tokenWithCompletion:(FIRMessagingFCMTokenFetchCompletion)completion {
546
  FIROptions *options = FIRApp.defaultApp.options;
547
  [self retrieveFCMTokenForSenderID:options.GCMSenderID completion:completion];
548
}
549
- (void)deleteTokenWithCompletion:(FIRMessagingDeleteFCMTokenCompletion)completion {
550
  FIROptions *options = FIRApp.defaultApp.options;
551
  [self deleteFCMTokenForSenderID:options.GCMSenderID completion:completion];
552
}
553
 
554
- (void)retrieveFCMTokenForSenderID:(nonnull NSString *)senderID
555
                         completion:(nonnull FIRMessagingFCMTokenFetchCompletion)completion {
556
  if (!senderID.length) {
557
    NSString *description = @"Couldn't fetch token because a Sender ID was not supplied. A valid "
558
                            @"Sender ID is required to fetch an FCM token";
559
    FIRMessagingLoggerError(kFIRMessagingMessageCodeSenderIDNotSuppliedForTokenFetch, @"%@",
560
                            description);
561
    if (completion) {
562
      NSError *error = [NSError messagingErrorWithCode:kFIRMessagingErrorCodeMissingAuthorizedEntity
563
                                         failureReason:description];
564
      completion(nil, error);
565
    }
566
    return;
567
  }
568
  NSDictionary *options = nil;
569
  if (self.APNSToken) {
570
    options = @{kFIRMessagingTokenOptionsAPNSKey : self.APNSToken};
571
  } else {
572
    FIRMessagingLoggerWarn(kFIRMessagingMessageCodeAPNSTokenNotAvailableDuringTokenFetch,
573
                           @"APNS device token not set before retrieving FCM Token for Sender ID "
574
                           @"'%@'. Notifications to this FCM Token will not be delivered over APNS."
575
                           @"Be sure to re-retrieve the FCM token once the APNS device token is "
576
                           @"set.",
577
                           senderID);
578
  }
579
  [self.tokenManager
580
      tokenWithAuthorizedEntity:senderID
581
                          scope:kFIRMessagingDefaultTokenScope
582
                        options:options
583
                        handler:^(NSString *_Nullable FCMToken, NSError *_Nullable error) {
584
                          if (completion) {
585
                            completion(FCMToken, error);
586
                          }
587
                        }];
588
}
589
 
590
- (void)deleteFCMTokenForSenderID:(nonnull NSString *)senderID
591
                       completion:(nonnull FIRMessagingDeleteFCMTokenCompletion)completion {
592
  if (!senderID.length) {
593
    NSString *description = @"Couldn't delete token because a Sender ID was not supplied. A "
594
                            @"valid Sender ID is required to delete an FCM token";
595
    FIRMessagingLoggerError(kFIRMessagingMessageCodeSenderIDNotSuppliedForTokenDelete, @"%@",
596
                            description);
597
    if (completion) {
598
      NSError *error = [NSError messagingErrorWithCode:kFIRMessagingErrorCodeInvalidRequest
599
                                         failureReason:description];
600
      completion(error);
601
    }
602
    return;
603
  }
604
  FIRMessaging_WEAKIFY(self);
605
  [self.installations
606
      installationIDWithCompletion:^(NSString *_Nullable identifier, NSError *_Nullable error) {
607
        FIRMessaging_STRONGIFY(self);
608
        if (error) {
609
          NSError *newError = [NSError messagingErrorWithCode:kFIRMessagingErrorCodeInvalidIdentity
610
                                                failureReason:@"Failed to get installation ID."];
611
          completion(newError);
612
        } else {
613
          [self.tokenManager deleteTokenWithAuthorizedEntity:senderID
614
                                                       scope:kFIRMessagingDefaultTokenScope
615
                                                  instanceID:identifier
616
                                                     handler:^(NSError *_Nullable error) {
617
                                                       if (completion) {
618
                                                         completion(error);
619
                                                       }
620
                                                     }];
621
        }
622
      }];
623
}
624
 
625
- (void)deleteDataWithCompletion:(void (^)(NSError *_Nullable))completion {
626
  FIRMessaging_WEAKIFY(self);
627
  [self.tokenManager deleteWithHandler:^(NSError *error) {
628
    FIRMessaging_STRONGIFY(self);
629
    if (error) {
630
      if (completion) {
631
        completion(error);
632
      }
633
      return;
634
    }
635
    // Only request new token if FCM auto initialization is
636
    // enabled.
637
    if ([self isAutoInitEnabled]) {
638
      // Deletion succeeds! Requesting new checkin, IID and token.
639
      [self tokenWithCompletion:^(NSString *_Nullable token, NSError *_Nullable error) {
640
        if (completion) {
641
          completion(error);
642
        }
643
      }];
644
      return;
645
    }
646
    if (completion) {
647
      completion(nil);
648
    }
649
  }];
650
}
651
 
652
#pragma mark - FIRMessagingDelegate helper methods
653
- (void)setDelegate:(id<FIRMessagingDelegate>)delegate {
654
  _delegate = delegate;
655
  [self validateDelegateConformsToTokenAvailabilityMethods];
656
}
657
 
658
// Check if the delegate conforms to |didReceiveRegistrationToken:|
659
// and display a warning to the developer if not.
660
// NOTE: Once |didReceiveRegistrationToken:| can be made a required method, this
661
// check can be removed.
662
- (void)validateDelegateConformsToTokenAvailabilityMethods {
663
  if (self.delegate && ![self.delegate respondsToSelector:@selector(messaging:
664
                                                              didReceiveRegistrationToken:)]) {
665
    FIRMessagingLoggerWarn(kFIRMessagingMessageCodeTokenDelegateMethodsNotImplemented,
666
                           @"The object %@ does not respond to "
667
                           @"-messaging:didReceiveRegistrationToken:. Please implement "
668
                           @"-messaging:didReceiveRegistrationToken: to be provided with an FCM "
669
                           @"token.",
670
                           self.delegate.description);
671
  }
672
}
673
 
674
- (void)notifyRefreshedFCMToken {
675
  __weak FIRMessaging *weakSelf = self;
676
  if (![NSThread isMainThread]) {
677
    dispatch_async(dispatch_get_main_queue(), ^{
678
      [weakSelf notifyRefreshedFCMToken];
679
    });
680
    return;
681
  }
682
  if ([self.delegate respondsToSelector:@selector(messaging:didReceiveRegistrationToken:)]) {
683
    [self.delegate messaging:self didReceiveRegistrationToken:self.tokenManager.defaultFCMToken];
684
  }
685
 
686
  // Should always trigger the token refresh notification when the delegate method is called
687
  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
688
  [center postNotificationName:FIRMessagingRegistrationTokenRefreshedNotification
689
                        object:self.tokenManager.defaultFCMToken];
690
}
691
 
692
#pragma mark - Topics
693
 
694
+ (NSString *)normalizeTopic:(NSString *)topic {
695
  if (!topic.length) {
696
    return nil;
697
  }
698
  if (![FIRMessagingPubSub hasTopicsPrefix:topic]) {
699
    topic = [FIRMessagingPubSub addPrefixToTopic:topic];
700
  }
701
  if ([FIRMessagingPubSub isValidTopicWithPrefix:topic]) {
702
    return [topic copy];
703
  }
704
  return nil;
705
}
706
 
707
- (void)subscribeToTopic:(NSString *)topic {
708
  [self subscribeToTopic:topic completion:nil];
709
}
710
 
711
- (void)subscribeToTopic:(NSString *)topic
712
              completion:(nullable FIRMessagingTopicOperationCompletion)completion {
713
  if ([FIRMessagingPubSub hasTopicsPrefix:topic]) {
714
    FIRMessagingLoggerWarn(kFIRMessagingMessageCodeTopicFormatIsDeprecated,
715
                           @"Format '%@' is deprecated. Only '%@' should be used in "
716
                           @"subscribeToTopic.",
717
                           topic, [FIRMessagingPubSub removePrefixFromTopic:topic]);
718
  }
719
  __weak FIRMessaging *weakSelf = self;
720
  [self
721
      retrieveFCMTokenForSenderID:self.tokenManager.fcmSenderID
722
                       completion:^(NSString *_Nullable FCMToken, NSError *_Nullable error) {
723
                         if (error) {
724
                           FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging010,
725
                                                   @"The subscription operation failed due to an "
726
                                                   @"error getting the FCM token: %@.",
727
                                                   error);
728
                           if (completion) {
729
                             completion(error);
730
                           }
731
                           return;
732
                         }
733
                         FIRMessaging *strongSelf = weakSelf;
734
                         NSString *normalizeTopic = [[strongSelf class] normalizeTopic:topic];
735
                         if (normalizeTopic.length) {
736
                           [strongSelf.pubsub subscribeToTopic:normalizeTopic handler:completion];
737
                           return;
738
                         }
739
                         NSString *failureReason = [NSString
740
                             stringWithFormat:@"Cannot parse topic name: '%@'. Will not subscribe.",
741
                                              topic];
742
                         FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging009, @"%@",
743
                                                 failureReason);
744
                         if (completion) {
745
                           completion([NSError
746
                               messagingErrorWithCode:kFIRMessagingErrorCodeInvalidTopicName
747
                                        failureReason:failureReason]);
748
                         }
749
                       }];
750
}
751
 
752
- (void)unsubscribeFromTopic:(NSString *)topic {
753
  [self unsubscribeFromTopic:topic completion:nil];
754
}
755
 
756
- (void)unsubscribeFromTopic:(NSString *)topic
757
                  completion:(nullable FIRMessagingTopicOperationCompletion)completion {
758
  if ([FIRMessagingPubSub hasTopicsPrefix:topic]) {
759
    FIRMessagingLoggerWarn(kFIRMessagingMessageCodeTopicFormatIsDeprecated,
760
                           @"Format '%@' is deprecated. Only '%@' should be used in "
761
                           @"unsubscribeFromTopic.",
762
                           topic, [FIRMessagingPubSub removePrefixFromTopic:topic]);
763
  }
764
  __weak FIRMessaging *weakSelf = self;
765
  [self retrieveFCMTokenForSenderID:self.tokenManager.fcmSenderID
766
                         completion:^(NSString *_Nullable FCMToken, NSError *_Nullable error) {
767
                           if (error) {
768
                             FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging012,
769
                                                     @"The unsubscription operation failed due to "
770
                                                     @"an error getting the FCM token: %@.",
771
                                                     error);
772
                             if (completion) {
773
                               completion(error);
774
                             }
775
                             return;
776
                           }
777
                           FIRMessaging *strongSelf = weakSelf;
778
                           NSString *normalizeTopic = [[strongSelf class] normalizeTopic:topic];
779
                           if (normalizeTopic.length) {
780
                             [strongSelf.pubsub unsubscribeFromTopic:normalizeTopic
781
                                                             handler:completion];
782
                             return;
783
                           }
784
                           NSString *failureReason = [NSString
785
                               stringWithFormat:
786
                                   @"Cannot parse topic name: '%@'. Will not unsubscribe.", topic];
787
                           FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging011, @"%@",
788
                                                   failureReason);
789
                           if (completion) {
790
                             completion([NSError
791
                                 messagingErrorWithCode:kFIRMessagingErrorCodeInvalidTopicName
792
                                          failureReason:failureReason]);
793
                           }
794
                         }];
795
}
796
 
797
#pragma mark - GULReachabilityDelegate
798
 
799
- (void)reachability:(GULReachabilityChecker *)reachability
800
       statusChanged:(GULReachabilityStatus)status {
801
  [self onNetworkStatusChanged];
802
}
803
 
804
#pragma mark - Network
805
 
806
- (void)onNetworkStatusChanged {
807
  if ([self isNetworkAvailable]) {
808
    [self.pubsub scheduleSync:YES];
809
  }
810
}
811
 
812
- (BOOL)isNetworkAvailable {
813
  GULReachabilityStatus status = self.reachability.reachabilityStatus;
814
  return (status == kGULReachabilityViaCellular || status == kGULReachabilityViaWifi);
815
}
816
 
817
- (FIRMessagingNetworkStatus)networkType {
818
  GULReachabilityStatus status = self.reachability.reachabilityStatus;
819
  if (![self isNetworkAvailable]) {
820
    return kFIRMessagingReachabilityNotReachable;
821
  } else if (status == kGULReachabilityViaCellular) {
822
    return kFIRMessagingReachabilityReachableViaWWAN;
823
  } else {
824
    return kFIRMessagingReachabilityReachableViaWiFi;
825
  }
826
}
827
 
828
#pragma mark - Notifications
829
 
830
- (void)defaultFCMTokenWasRefreshed:(NSNotification *)notification {
831
  if (notification.object && ![notification.object isKindOfClass:[NSString class]]) {
832
    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeMessaging015,
833
                            @"Invalid default FCM token type %@",
834
                            NSStringFromClass([notification.object class]));
835
    return;
836
  }
837
  NSString *newToken = [(NSString *)notification.object copy];
838
  [self updateDefaultFCMToken:newToken];
839
}
840
 
841
- (void)updateDefaultFCMToken:(NSString *)defaultFCMToken {
842
  NSString *oldToken = self.tokenManager.defaultFCMToken;
843
  NSString *newToken = defaultFCMToken;
844
  if ([self.tokenManager hasTokenChangedFromOldToken:oldToken toNewToken:newToken]) {
845
    // Make sure to set default token first before notifying others.
846
    [self.tokenManager saveDefaultTokenInfoInKeychain:newToken];
847
    [self notifyDelegateOfFCMTokenAvailability];
848
    [self.pubsub scheduleSync:YES];
849
  }
850
}
851
 
852
- (void)notifyDelegateOfFCMTokenAvailability {
853
  __weak FIRMessaging *weakSelf = self;
854
  if (![NSThread isMainThread]) {
855
    dispatch_async(dispatch_get_main_queue(), ^{
856
      [weakSelf notifyDelegateOfFCMTokenAvailability];
857
    });
858
    return;
859
  }
860
  if ([self.delegate respondsToSelector:@selector(messaging:didReceiveRegistrationToken:)]) {
861
    [self.delegate messaging:self didReceiveRegistrationToken:self.tokenManager.defaultFCMToken];
862
  }
863
  // Should always trigger the token refresh notification when the delegate method is called
864
  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
865
  [center postNotificationName:FIRMessagingRegistrationTokenRefreshedNotification
866
                        object:self.tokenManager.defaultFCMToken];
867
}
868
 
869
#pragma mark - Application Support Directory
870
 
871
+ (BOOL)hasSubDirectory:(NSString *)subDirectoryName {
872
  NSString *subDirectoryPath = [self pathForSubDirectory:subDirectoryName];
873
  BOOL isDirectory;
874
  if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
875
                                            isDirectory:&isDirectory]) {
876
    return NO;
877
  } else if (!isDirectory) {
878
    return NO;
879
  }
880
  return YES;
881
}
882
 
883
+ (NSString *)pathForSubDirectory:(NSString *)subDirectoryName {
884
  NSArray *directoryPaths =
885
      NSSearchPathForDirectoriesInDomains(FIRMessagingSupportedDirectory(), NSUserDomainMask, YES);
886
  NSString *dirPath = directoryPaths.lastObject;
887
  NSArray *components = @[ dirPath, subDirectoryName ];
888
  return [NSString pathWithComponents:components];
889
}
890
 
891
+ (BOOL)createSubDirectory:(NSString *)subDirectoryName {
892
  NSString *subDirectoryPath = [self pathForSubDirectory:subDirectoryName];
893
  BOOL hasSubDirectory;
894
 
895
  if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
896
                                            isDirectory:&hasSubDirectory]) {
897
    NSError *error;
898
    [[NSFileManager defaultManager] createDirectoryAtPath:subDirectoryPath
899
                              withIntermediateDirectories:YES
900
                                               attributes:nil
901
                                                    error:&error];
902
    if (error) {
903
      FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging017,
904
                              @"Cannot create directory %@, error: %@", subDirectoryPath, error);
905
      return NO;
906
    }
907
  } else {
908
    if (!hasSubDirectory) {
909
      FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging018,
910
                              @"Found file instead of directory at %@", subDirectoryPath);
911
      return NO;
912
    }
913
  }
914
  return YES;
915
}
916
 
917
#pragma mark - Locales
918
 
919
+ (NSString *)currentLocale {
920
  NSArray *locales = [self firebaseLocales];
921
  NSArray *preferredLocalizations =
922
      [NSBundle preferredLocalizationsFromArray:locales
923
                                 forPreferences:[NSLocale preferredLanguages]];
924
  NSString *legalDocsLanguage = [preferredLocalizations firstObject];
925
  // Use en as the default language
926
  return legalDocsLanguage ? legalDocsLanguage : @"en";
927
}
928
 
929
+ (NSArray *)firebaseLocales {
930
  NSMutableArray *locales = [NSMutableArray array];
931
  NSDictionary *localesMap = [self firebaselocalesMap];
932
  for (NSString *key in localesMap) {
933
    [locales addObjectsFromArray:localesMap[key]];
934
  }
935
  return locales;
936
}
937
 
938
+ (NSDictionary *)firebaselocalesMap {
939
  return @{
940
    // Albanian
941
    @"sq" : @[ @"sq_AL" ],
942
    // Belarusian
943
    @"be" : @[ @"be_BY" ],
944
    // Bulgarian
945
    @"bg" : @[ @"bg_BG" ],
946
    // Catalan
947
    @"ca" : @[ @"ca", @"ca_ES" ],
948
    // Croatian
949
    @"hr" : @[ @"hr", @"hr_HR" ],
950
    // Czech
951
    @"cs" : @[ @"cs", @"cs_CZ" ],
952
    // Danish
953
    @"da" : @[ @"da", @"da_DK" ],
954
    // Estonian
955
    @"et" : @[ @"et_EE" ],
956
    // Finnish
957
    @"fi" : @[ @"fi", @"fi_FI" ],
958
    // Hebrew
959
    @"he" : @[ @"he", @"iw_IL" ],
960
    // Hindi
961
    @"hi" : @[ @"hi_IN" ],
962
    // Hungarian
963
    @"hu" : @[ @"hu", @"hu_HU" ],
964
    // Icelandic
965
    @"is" : @[ @"is_IS" ],
966
    // Indonesian
967
    @"id" : @[ @"id", @"in_ID", @"id_ID" ],
968
    // Irish
969
    @"ga" : @[ @"ga_IE" ],
970
    // Korean
971
    @"ko" : @[ @"ko", @"ko_KR", @"ko-KR" ],
972
    // Latvian
973
    @"lv" : @[ @"lv_LV" ],
974
    // Lithuanian
975
    @"lt" : @[ @"lt_LT" ],
976
    // Macedonian
977
    @"mk" : @[ @"mk_MK" ],
978
    // Malay
979
    @"ms" : @[ @"ms_MY" ],
980
    // Maltese
981
    @"mt" : @[ @"mt_MT" ],
982
    // Polish
983
    @"pl" : @[ @"pl", @"pl_PL", @"pl-PL" ],
984
    // Romanian
985
    @"ro" : @[ @"ro", @"ro_RO" ],
986
    // Russian
987
    @"ru" : @[ @"ru_RU", @"ru", @"ru_BY", @"ru_KZ", @"ru-RU" ],
988
    // Slovak
989
    @"sk" : @[ @"sk", @"sk_SK" ],
990
    // Slovenian
991
    @"sl" : @[ @"sl_SI" ],
992
    // Swedish
993
    @"sv" : @[ @"sv", @"sv_SE", @"sv-SE" ],
994
    // Turkish
995
    @"tr" : @[ @"tr", @"tr-TR", @"tr_TR" ],
996
    // Ukrainian
997
    @"uk" : @[ @"uk", @"uk_UA" ],
998
    // Vietnamese
999
    @"vi" : @[ @"vi", @"vi_VN" ],
1000
    // The following are groups of locales or locales that sub-divide a
1001
    // language).
1002
    // Arabic
1003
    @"ar" : @[
1004
      @"ar",    @"ar_DZ", @"ar_BH", @"ar_EG", @"ar_IQ", @"ar_JO", @"ar_KW",
1005
      @"ar_LB", @"ar_LY", @"ar_MA", @"ar_OM", @"ar_QA", @"ar_SA", @"ar_SD",
1006
      @"ar_SY", @"ar_TN", @"ar_AE", @"ar_YE", @"ar_GB", @"ar-IQ", @"ar_US"
1007
    ],
1008
    // Simplified Chinese
1009
    @"zh_Hans" : @[ @"zh_CN", @"zh_SG", @"zh-Hans" ],
1010
    // Traditional Chinese
1011
    @"zh_Hant" : @[ @"zh_HK", @"zh_TW", @"zh-Hant", @"zh-HK", @"zh-TW" ],
1012
    // Dutch
1013
    @"nl" : @[ @"nl", @"nl_BE", @"nl_NL", @"nl-NL" ],
1014
    // English
1015
    @"en" : @[
1016
      @"en",    @"en_AU", @"en_CA", @"en_IN", @"en_IE", @"en_MT", @"en_NZ", @"en_PH",
1017
      @"en_SG", @"en_ZA", @"en_GB", @"en_US", @"en_AE", @"en-AE", @"en_AS", @"en-AU",
1018
      @"en_BD", @"en-CA", @"en_EG", @"en_ES", @"en_GB", @"en-GB", @"en_HK", @"en_ID",
1019
      @"en-IN", @"en_NG", @"en-PH", @"en_PK", @"en-SG", @"en-US"
1020
    ],
1021
    // French
1022
 
1023
    @"fr" :
1024
        @[ @"fr", @"fr_BE", @"fr_CA", @"fr_FR", @"fr_LU", @"fr_CH", @"fr-CA", @"fr-FR", @"fr_MA" ],
1025
    // German
1026
    @"de" : @[ @"de", @"de_AT", @"de_DE", @"de_LU", @"de_CH", @"de-DE" ],
1027
    // Greek
1028
    @"el" : @[ @"el", @"el_CY", @"el_GR" ],
1029
    // Italian
1030
    @"it" : @[ @"it", @"it_IT", @"it_CH", @"it-IT" ],
1031
    // Japanese
1032
    @"ja" : @[ @"ja", @"ja_JP", @"ja_JP_JP", @"ja-JP" ],
1033
    // Norwegian
1034
    @"no" : @[ @"nb", @"no_NO", @"no_NO_NY", @"nb_NO" ],
1035
    // Brazilian Portuguese
1036
    @"pt_BR" : @[ @"pt_BR", @"pt-BR" ],
1037
    // European Portuguese
1038
    @"pt_PT" : @[ @"pt", @"pt_PT", @"pt-PT" ],
1039
    // Serbian
1040
    @"sr" : @[ @"sr_BA", @"sr_ME", @"sr_RS", @"sr_Latn_BA", @"sr_Latn_ME", @"sr_Latn_RS" ],
1041
    // European Spanish
1042
    @"es_ES" : @[ @"es", @"es_ES", @"es-ES" ],
1043
    // Mexican Spanish
1044
    @"es_MX" : @[ @"es-MX", @"es_MX", @"es_US", @"es-US" ],
1045
    // Latin American Spanish
1046
    @"es_419" : @[
1047
      @"es_AR", @"es_BO", @"es_CL", @"es_CO", @"es_CR", @"es_DO", @"es_EC",
1048
      @"es_SV", @"es_GT", @"es_HN", @"es_NI", @"es_PA", @"es_PY", @"es_PE",
1049
      @"es_PR", @"es_UY", @"es_VE", @"es-AR", @"es-CL", @"es-CO"
1050
    ],
1051
    // Thai
1052
    @"th" : @[ @"th", @"th_TH", @"th_TH_TH" ],
1053
  };
1054
}
1055
 
1056
#pragma mark - Utilities used by InstanceID
1057
 
1058
+ (NSString *)FIRMessagingSDKVersion {
1059
  return FIRFirebaseVersion();
1060
}
1061
 
1062
+ (NSString *)FIRMessagingSDKCurrentLocale {
1063
  return [self currentLocale];
1064
}
1065
 
1066
@end