FIRInstanceID.m 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158
  1. /*
  2. * Copyright 2019 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "FIRInstanceID.h"
  17. #import "FirebaseInstallations/Source/Library/Private/FirebaseInstallationsInternal.h"
  18. #import "FIRInstanceID+Private.h"
  19. #import "FIRInstanceIDAuthService.h"
  20. #import "FIRInstanceIDCheckinPreferences.h"
  21. #import "FIRInstanceIDCombinedHandler.h"
  22. #import "FIRInstanceIDConstants.h"
  23. #import "FIRInstanceIDDefines.h"
  24. #import "FIRInstanceIDLogger.h"
  25. #import "FIRInstanceIDStore.h"
  26. #import "FIRInstanceIDTokenInfo.h"
  27. #import "FIRInstanceIDTokenManager.h"
  28. #import "FIRInstanceIDUtilities.h"
  29. #import "FIRInstanceIDVersionUtilities.h"
  30. #import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
  31. #import "GoogleUtilities/Environment/Private/GULAppEnvironmentUtil.h"
  32. #import "GoogleUtilities/UserDefaults/Private/GULUserDefaults.h"
  33. #import "NSError+FIRInstanceID.h"
  34. // Public constants
  35. NSString *const kFIRInstanceIDScopeFirebaseMessaging = @"fcm";
  36. #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
  37. const NSNotificationName kFIRInstanceIDTokenRefreshNotification =
  38. @"com.firebase.iid.notif.refresh-token";
  39. #else
  40. NSString *const kFIRInstanceIDTokenRefreshNotification = @"com.firebase.iid.notif.refresh-token";
  41. #endif // defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
  42. NSString *const kFIRInstanceIDInvalidNilHandlerError = @"Invalid nil handler.";
  43. // Private constants
  44. int64_t const kMaxRetryIntervalForDefaultTokenInSeconds = 20 * 60; // 20 minutes
  45. int64_t const kMinRetryIntervalForDefaultTokenInSeconds = 10; // 10 seconds
  46. // we retry only a max 5 times.
  47. // TODO(chliangGoogle): If we still fail we should listen for the network change notification
  48. // since GCM would have started Reachability. We only start retrying after we see a configuration
  49. // change.
  50. NSInteger const kMaxRetryCountForDefaultToken = 5;
  51. #if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
  52. static NSString *const kEntitlementsAPSEnvironmentKey = @"Entitlements.aps-environment";
  53. #else
  54. static NSString *const kEntitlementsAPSEnvironmentKey =
  55. @"Entitlements.com.apple.developer.aps-environment";
  56. #endif
  57. static NSString *const kAPSEnvironmentDevelopmentValue = @"development";
  58. /// FIRMessaging selector that returns the current FIRMessaging auto init
  59. /// enabled flag.
  60. static NSString *const kFIRInstanceIDFCMSelectorAutoInitEnabled =
  61. @"isAutoInitEnabledWithUserDefaults:";
  62. static NSString *const kFIRInstanceIDAPNSTokenType = @"APNSTokenType";
  63. static NSString *const kFIRIIDAppReadyToConfigureSDKNotification =
  64. @"FIRAppReadyToConfigureSDKNotification";
  65. static NSString *const kFIRIIDAppNameKey = @"FIRAppNameKey";
  66. static NSString *const kFIRIIDErrorDomain = @"com.firebase.instanceid";
  67. static NSString *const kFIRIIDServiceInstanceID = @"InstanceID";
  68. /**
  69. * The APNS token type for the app. If the token type is set to `UNKNOWN`
  70. * InstanceID will implicitly try to figure out what the actual token type
  71. * is from the provisioning profile.
  72. * This must match FIRMessagingAPNSTokenType in FIRMessaging.h
  73. */
  74. typedef NS_ENUM(NSInteger, FIRInstanceIDAPNSTokenType) {
  75. /// Unknown token type.
  76. FIRInstanceIDAPNSTokenTypeUnknown,
  77. /// Sandbox token type.
  78. FIRInstanceIDAPNSTokenTypeSandbox,
  79. /// Production token type.
  80. FIRInstanceIDAPNSTokenTypeProd,
  81. } NS_SWIFT_NAME(InstanceIDAPNSTokenType);
  82. @interface FIRInstanceIDResult ()
  83. @property(nonatomic, readwrite, copy) NSString *instanceID;
  84. @property(nonatomic, readwrite, copy) NSString *token;
  85. @end
  86. @interface FIRInstanceID ()
  87. // FIRApp configuration objects.
  88. @property(nonatomic, readwrite, copy) NSString *fcmSenderID;
  89. @property(nonatomic, readwrite, copy) NSString *firebaseAppID;
  90. // Raw APNS token data
  91. @property(nonatomic, readwrite, strong) NSData *apnsTokenData;
  92. @property(nonatomic, readwrite) FIRInstanceIDAPNSTokenType apnsTokenType;
  93. // String-based, internal representation of APNS token
  94. @property(nonatomic, readwrite, copy) NSString *APNSTupleString;
  95. // Token fetched from the server automatically for the default app.
  96. @property(nonatomic, readwrite, copy) NSString *defaultFCMToken;
  97. @property(nonatomic, readwrite, strong) FIRInstanceIDTokenManager *tokenManager;
  98. @property(nonatomic, readwrite, strong) FIRInstallations *installations;
  99. // backoff and retry for default token
  100. @property(nonatomic, readwrite, assign) NSInteger retryCountForDefaultToken;
  101. @property(atomic, strong, nullable)
  102. FIRInstanceIDCombinedHandler<NSString *> *defaultTokenFetchHandler;
  103. /// A cached value of FID. Should be used only for `-[FIRInstanceID appInstanceID:]`.
  104. @property(atomic, copy, nullable) NSString *firebaseInstallationsID;
  105. @end
  106. // InstanceID doesn't provide any functionality to other components,
  107. // so it provides a private, empty protocol that it conforms to and use it for registration.
  108. @protocol FIRInstanceIDInstanceProvider
  109. @end
  110. @interface FIRInstanceID () <FIRInstanceIDInstanceProvider, FIRLibrary>
  111. @end
  112. @implementation FIRInstanceIDResult
  113. - (id)copyWithZone:(NSZone *)zone {
  114. FIRInstanceIDResult *result = [[[self class] allocWithZone:zone] init];
  115. result.instanceID = self.instanceID;
  116. result.token = self.token;
  117. return result;
  118. }
  119. @end
  120. @implementation FIRInstanceID
  121. // File static to support InstanceID tests that call [FIRInstanceID instanceID] after
  122. // [FIRInstanceID instanceIDForTests].
  123. static FIRInstanceID *gInstanceID;
  124. + (instancetype)instanceID {
  125. // If the static instance was created, return it. This should only be set in tests and we should
  126. // eventually use proper dependency injection for a better test structure.
  127. if (gInstanceID != nil) {
  128. return gInstanceID;
  129. }
  130. FIRApp *defaultApp = [FIRApp defaultApp]; // Missing configure will be logged here.
  131. FIRInstanceID *instanceID =
  132. (FIRInstanceID *)FIR_COMPONENT(FIRInstanceIDInstanceProvider, defaultApp.container);
  133. return instanceID;
  134. }
  135. - (instancetype)initPrivately {
  136. self = [super init];
  137. if (self != nil) {
  138. // Use automatic detection of sandbox, unless otherwise set by developer
  139. _apnsTokenType = FIRInstanceIDAPNSTokenTypeUnknown;
  140. }
  141. return self;
  142. }
  143. + (FIRInstanceID *)instanceIDForTests {
  144. gInstanceID = [[FIRInstanceID alloc] initPrivately];
  145. [gInstanceID start];
  146. return gInstanceID;
  147. }
  148. - (void)dealloc {
  149. [[NSNotificationCenter defaultCenter] removeObserver:self];
  150. }
  151. #pragma mark - Tokens
  152. - (NSString *)token {
  153. if (!self.fcmSenderID.length) {
  154. return nil;
  155. }
  156. NSString *cachedToken = [self cachedTokenIfAvailable];
  157. if (cachedToken) {
  158. return cachedToken;
  159. } else {
  160. // If we've never had a cached default token, we should fetch one because unrelatedly,
  161. // this request will help us determine whether the locally-generated Instance ID keypair is not
  162. // unique, and therefore generate a new one.
  163. [self defaultTokenWithHandler:nil];
  164. return nil;
  165. }
  166. }
  167. - (void)instanceIDWithHandler:(FIRInstanceIDResultHandler)handler {
  168. FIRInstanceID_WEAKIFY(self);
  169. [self getIDWithHandler:^(NSString *identity, NSError *error) {
  170. FIRInstanceID_STRONGIFY(self);
  171. // This is in main queue already
  172. if (error) {
  173. if (handler) {
  174. handler(nil, error);
  175. }
  176. return;
  177. }
  178. FIRInstanceIDResult *result = [[FIRInstanceIDResult alloc] init];
  179. result.instanceID = identity;
  180. NSString *cachedToken = [self cachedTokenIfAvailable];
  181. if (cachedToken) {
  182. if (handler) {
  183. result.token = cachedToken;
  184. handler(result, nil);
  185. }
  186. // If no handler, simply return since client has generated iid and token.
  187. return;
  188. }
  189. [self defaultTokenWithHandler:^(NSString *_Nullable token, NSError *_Nullable error) {
  190. if (handler) {
  191. if (error) {
  192. handler(nil, error);
  193. return;
  194. }
  195. result.token = token;
  196. handler(result, nil);
  197. }
  198. }];
  199. }];
  200. }
  201. - (NSString *)cachedTokenIfAvailable {
  202. FIRInstanceIDTokenInfo *cachedTokenInfo =
  203. [self.tokenManager cachedTokenInfoWithAuthorizedEntity:self.fcmSenderID
  204. scope:kFIRInstanceIDDefaultTokenScope];
  205. return cachedTokenInfo.token;
  206. }
  207. - (void)setDefaultFCMToken:(NSString *)defaultFCMToken {
  208. // Sending this notification out will ensure that FIRMessaging and FIRInstanceID has the updated
  209. // default FCM token.
  210. // Only notify of token refresh if we have a new valid token that's different than before
  211. if ((defaultFCMToken.length && _defaultFCMToken.length &&
  212. ![defaultFCMToken isEqualToString:_defaultFCMToken]) ||
  213. defaultFCMToken.length != _defaultFCMToken.length) {
  214. NSNotification *tokenRefreshNotification =
  215. [NSNotification notificationWithName:kFIRInstanceIDTokenRefreshNotification
  216. object:[defaultFCMToken copy]];
  217. [[NSNotificationQueue defaultQueue] enqueueNotification:tokenRefreshNotification
  218. postingStyle:NSPostASAP];
  219. }
  220. _defaultFCMToken = defaultFCMToken;
  221. }
  222. - (void)tokenWithAuthorizedEntity:(NSString *)authorizedEntity
  223. scope:(NSString *)scope
  224. options:(NSDictionary *)options
  225. handler:(FIRInstanceIDTokenHandler)handler {
  226. if (!handler) {
  227. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID000,
  228. kFIRInstanceIDInvalidNilHandlerError);
  229. return;
  230. }
  231. // Add internal options
  232. NSMutableDictionary *tokenOptions = [NSMutableDictionary dictionary];
  233. if (options.count) {
  234. [tokenOptions addEntriesFromDictionary:options];
  235. }
  236. NSString *APNSKey = kFIRInstanceIDTokenOptionsAPNSKey;
  237. NSString *serverTypeKey = kFIRInstanceIDTokenOptionsAPNSIsSandboxKey;
  238. if (tokenOptions[APNSKey] != nil && tokenOptions[serverTypeKey] == nil) {
  239. // APNS key was given, but server type is missing. Supply the server type with automatic
  240. // checking. This can happen when the token is requested from FCM, which does not include a
  241. // server type during its request.
  242. tokenOptions[serverTypeKey] = @([self isSandboxApp]);
  243. }
  244. if (self.firebaseAppID) {
  245. tokenOptions[kFIRInstanceIDTokenOptionsFirebaseAppIDKey] = self.firebaseAppID;
  246. }
  247. // comparing enums to ints directly throws a warning
  248. FIRInstanceIDErrorCode noError = INT_MAX;
  249. FIRInstanceIDErrorCode errorCode = noError;
  250. if (FIRInstanceIDIsValidGCMScope(scope) && !tokenOptions[APNSKey]) {
  251. errorCode = kFIRInstanceIDErrorCodeMissingAPNSToken;
  252. } else if (FIRInstanceIDIsValidGCMScope(scope) &&
  253. ![tokenOptions[APNSKey] isKindOfClass:[NSData class]]) {
  254. errorCode = kFIRInstanceIDErrorCodeInvalidRequest;
  255. } else if (![authorizedEntity length]) {
  256. errorCode = kFIRInstanceIDErrorCodeInvalidAuthorizedEntity;
  257. } else if (![scope length]) {
  258. errorCode = kFIRInstanceIDErrorCodeInvalidScope;
  259. } else if (!self.installations) {
  260. errorCode = kFIRInstanceIDErrorCodeInvalidStart;
  261. }
  262. FIRInstanceIDTokenHandler newHandler = ^(NSString *token, NSError *error) {
  263. if (!error && [self isDefaultTokenWithAuthorizedEntity:authorizedEntity scope:scope]) {
  264. // The local cache should be updated as it is critical for sending token updates.
  265. self.defaultFCMToken = token;
  266. }
  267. dispatch_async(dispatch_get_main_queue(), ^{
  268. handler(token, error);
  269. });
  270. };
  271. if (errorCode != noError) {
  272. newHandler(nil, [NSError errorWithFIRInstanceIDErrorCode:errorCode]);
  273. return;
  274. }
  275. FIRInstanceID_WEAKIFY(self);
  276. FIRInstanceIDAuthService *authService = self.tokenManager.authService;
  277. [authService fetchCheckinInfoWithHandler:^(FIRInstanceIDCheckinPreferences *preferences,
  278. NSError *error) {
  279. FIRInstanceID_STRONGIFY(self);
  280. if (error) {
  281. newHandler(nil, error);
  282. return;
  283. }
  284. FIRInstanceID_WEAKIFY(self);
  285. [self.installations installationIDWithCompletion:^(NSString *_Nullable identifier,
  286. NSError *_Nullable error) {
  287. FIRInstanceID_STRONGIFY(self);
  288. if (error) {
  289. NSError *newError =
  290. [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidKeyPair];
  291. newHandler(nil, newError);
  292. } else {
  293. FIRInstanceIDTokenInfo *cachedTokenInfo =
  294. [self.tokenManager cachedTokenInfoWithAuthorizedEntity:authorizedEntity scope:scope];
  295. if (cachedTokenInfo) {
  296. FIRInstanceIDAPNSInfo *optionsAPNSInfo =
  297. [[FIRInstanceIDAPNSInfo alloc] initWithTokenOptionsDictionary:tokenOptions];
  298. // Check if APNS Info is changed
  299. if ((!cachedTokenInfo.APNSInfo && !optionsAPNSInfo) ||
  300. [cachedTokenInfo.APNSInfo isEqualToAPNSInfo:optionsAPNSInfo]) {
  301. // check if token is fresh
  302. if ([cachedTokenInfo isFreshWithIID:identifier]) {
  303. newHandler(cachedTokenInfo.token, nil);
  304. return;
  305. }
  306. }
  307. }
  308. [self.tokenManager fetchNewTokenWithAuthorizedEntity:[authorizedEntity copy]
  309. scope:[scope copy]
  310. instanceID:identifier
  311. options:tokenOptions
  312. handler:newHandler];
  313. }
  314. }];
  315. }];
  316. }
  317. - (void)deleteTokenWithAuthorizedEntity:(NSString *)authorizedEntity
  318. scope:(NSString *)scope
  319. handler:(FIRInstanceIDDeleteTokenHandler)handler {
  320. if (!handler) {
  321. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID001,
  322. kFIRInstanceIDInvalidNilHandlerError);
  323. return;
  324. }
  325. // comparing enums to ints directly throws a warning
  326. FIRInstanceIDErrorCode noError = INT_MAX;
  327. FIRInstanceIDErrorCode errorCode = noError;
  328. if (![authorizedEntity length]) {
  329. errorCode = kFIRInstanceIDErrorCodeInvalidAuthorizedEntity;
  330. } else if (![scope length]) {
  331. errorCode = kFIRInstanceIDErrorCodeInvalidScope;
  332. } else if (!self.installations) {
  333. errorCode = kFIRInstanceIDErrorCodeInvalidStart;
  334. }
  335. FIRInstanceIDDeleteTokenHandler newHandler = ^(NSError *error) {
  336. // If a default token is deleted successfully, reset the defaultFCMToken too.
  337. if (!error && [self isDefaultTokenWithAuthorizedEntity:authorizedEntity scope:scope]) {
  338. self.defaultFCMToken = nil;
  339. }
  340. dispatch_async(dispatch_get_main_queue(), ^{
  341. handler(error);
  342. });
  343. };
  344. if (errorCode != noError) {
  345. newHandler([NSError errorWithFIRInstanceIDErrorCode:errorCode]);
  346. return;
  347. }
  348. FIRInstanceID_WEAKIFY(self);
  349. FIRInstanceIDAuthService *authService = self.tokenManager.authService;
  350. [authService
  351. fetchCheckinInfoWithHandler:^(FIRInstanceIDCheckinPreferences *preferences, NSError *error) {
  352. FIRInstanceID_STRONGIFY(self);
  353. if (error) {
  354. newHandler(error);
  355. return;
  356. }
  357. FIRInstanceID_WEAKIFY(self);
  358. [self.installations installationIDWithCompletion:^(NSString *_Nullable identifier,
  359. NSError *_Nullable error) {
  360. FIRInstanceID_STRONGIFY(self);
  361. if (error) {
  362. NSError *newError =
  363. [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidKeyPair];
  364. newHandler(newError);
  365. } else {
  366. [self.tokenManager deleteTokenWithAuthorizedEntity:authorizedEntity
  367. scope:scope
  368. instanceID:identifier
  369. handler:newHandler];
  370. }
  371. }];
  372. }];
  373. }
  374. #pragma mark - Identity
  375. - (void)getIDWithHandler:(FIRInstanceIDHandler)handler {
  376. if (!handler) {
  377. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID003,
  378. kFIRInstanceIDInvalidNilHandlerError);
  379. return;
  380. }
  381. FIRInstanceID_WEAKIFY(self);
  382. [self.installations
  383. installationIDWithCompletion:^(NSString *_Nullable identifier, NSError *_Nullable error) {
  384. FIRInstanceID_STRONGIFY(self);
  385. // When getID is explicitly called, trigger getToken to make sure token always exists.
  386. // This is to avoid ID conflict (ID is not checked for conflict until we generate a token)
  387. if (identifier) {
  388. [self token];
  389. }
  390. handler(identifier, error);
  391. }];
  392. }
  393. - (void)deleteIDWithHandler:(FIRInstanceIDDeleteHandler)handler {
  394. if (!handler) {
  395. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID004,
  396. kFIRInstanceIDInvalidNilHandlerError);
  397. return;
  398. }
  399. void (^callHandlerOnMainThread)(NSError *) = ^(NSError *error) {
  400. if ([NSThread isMainThread]) {
  401. handler(error);
  402. return;
  403. }
  404. dispatch_async(dispatch_get_main_queue(), ^{
  405. handler(error);
  406. });
  407. };
  408. if (!self.installations) {
  409. FIRInstanceIDErrorCode error = kFIRInstanceIDErrorCodeInvalidStart;
  410. callHandlerOnMainThread([NSError errorWithFIRInstanceIDErrorCode:error]);
  411. return;
  412. }
  413. FIRInstanceID_WEAKIFY(self);
  414. void (^deleteTokensHandler)(NSError *) = ^void(NSError *error) {
  415. FIRInstanceID_STRONGIFY(self);
  416. if (error) {
  417. callHandlerOnMainThread(error);
  418. return;
  419. }
  420. [self deleteIdentityWithHandler:^(NSError *error) {
  421. callHandlerOnMainThread(error);
  422. }];
  423. };
  424. [self.installations
  425. installationIDWithCompletion:^(NSString *_Nullable identifier, NSError *_Nullable error) {
  426. FIRInstanceID_STRONGIFY(self);
  427. if (error) {
  428. NSError *newError =
  429. [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidKeyPair];
  430. callHandlerOnMainThread(newError);
  431. } else {
  432. [self.tokenManager deleteAllTokensWithInstanceID:identifier handler:deleteTokensHandler];
  433. }
  434. }];
  435. }
  436. - (void)notifyIdentityReset {
  437. [self deleteIdentityWithHandler:nil];
  438. }
  439. // Delete all the local cache checkin, IID and token.
  440. - (void)deleteIdentityWithHandler:(FIRInstanceIDDeleteHandler)handler {
  441. // Delete tokens.
  442. [self.tokenManager deleteAllTokensLocallyWithHandler:^(NSError *deleteTokenError) {
  443. // Reset FCM token.
  444. self.defaultFCMToken = nil;
  445. if (deleteTokenError) {
  446. if (handler) {
  447. handler(deleteTokenError);
  448. }
  449. return;
  450. }
  451. // Delete Instance ID.
  452. [self.installations deleteWithCompletion:^(NSError *_Nullable error) {
  453. if (error) {
  454. if (handler) {
  455. handler(error);
  456. }
  457. return;
  458. }
  459. [self.tokenManager.authService resetCheckinWithHandler:^(NSError *error) {
  460. if (error) {
  461. if (handler) {
  462. handler(error);
  463. }
  464. return;
  465. }
  466. // Only request new token if FCM auto initialization is
  467. // enabled.
  468. if ([self isFCMAutoInitEnabled]) {
  469. // Deletion succeeds! Requesting new checkin, IID and token.
  470. // TODO(chliangGoogle) see if dispatch_after is necessary
  471. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)),
  472. dispatch_get_main_queue(), ^{
  473. [self defaultTokenWithHandler:nil];
  474. });
  475. }
  476. if (handler) {
  477. handler(nil);
  478. }
  479. }];
  480. }];
  481. }];
  482. }
  483. #pragma mark - Checkin
  484. - (BOOL)tryToLoadValidCheckinInfo {
  485. FIRInstanceIDCheckinPreferences *checkinPreferences =
  486. [self.tokenManager.authService checkinPreferences];
  487. return [checkinPreferences hasValidCheckinInfo];
  488. }
  489. - (NSString *)deviceAuthID {
  490. return [self.tokenManager.authService checkinPreferences].deviceID;
  491. }
  492. - (NSString *)secretToken {
  493. return [self.tokenManager.authService checkinPreferences].secretToken;
  494. }
  495. - (NSString *)versionInfo {
  496. return [self.tokenManager.authService checkinPreferences].versionInfo;
  497. }
  498. #pragma mark - Config
  499. + (void)load {
  500. [FIRApp registerInternalLibrary:(Class<FIRLibrary>)self
  501. withName:@"fire-iid"
  502. withVersion:FIRInstanceIDCurrentLibraryVersion()];
  503. }
  504. + (nonnull NSArray<FIRComponent *> *)componentsToRegister {
  505. FIRComponentCreationBlock creationBlock =
  506. ^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) {
  507. // InstanceID only works with the default app.
  508. if (!container.app.isDefaultApp) {
  509. // Only configure for the default FIRApp.
  510. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeFIRApp002,
  511. @"Firebase Instance ID only works with the default app.");
  512. return nil;
  513. }
  514. // Ensure it's cached so it returns the same instance every time instanceID is called.
  515. *isCacheable = YES;
  516. FIRInstanceID *instanceID = [[FIRInstanceID alloc] initPrivately];
  517. [instanceID start];
  518. [instanceID configureInstanceIDWithOptions:container.app.options];
  519. return instanceID;
  520. };
  521. FIRComponent *instanceIDProvider =
  522. [FIRComponent componentWithProtocol:@protocol(FIRInstanceIDInstanceProvider)
  523. instantiationTiming:FIRInstantiationTimingEagerInDefaultApp
  524. dependencies:@[]
  525. creationBlock:creationBlock];
  526. return @[ instanceIDProvider ];
  527. }
  528. - (void)configureInstanceIDWithOptions:(FIROptions *)options {
  529. NSString *GCMSenderID = options.GCMSenderID;
  530. if (!GCMSenderID.length) {
  531. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeFIRApp000,
  532. @"Firebase not set up correctly, nil or empty senderID.");
  533. [NSException raise:kFIRIIDErrorDomain
  534. format:@"Could not configure Firebase InstanceID. GCMSenderID must not be nil or "
  535. @"empty."];
  536. }
  537. self.fcmSenderID = GCMSenderID;
  538. self.firebaseAppID = options.googleAppID;
  539. [self updateFirebaseInstallationID];
  540. // FCM generates a FCM token during app start for sending push notification to device.
  541. // This is not needed for app extension except for watch.
  542. #if TARGET_OS_WATCH
  543. [self didCompleteConfigure];
  544. #else
  545. if (![GULAppEnvironmentUtil isAppExtension]) {
  546. [self didCompleteConfigure];
  547. }
  548. #endif
  549. }
  550. // This is used to start any operations when we receive FirebaseSDK setup notification
  551. // from FIRCore.
  552. - (void)didCompleteConfigure {
  553. NSString *cachedToken = [self cachedTokenIfAvailable];
  554. // When there is a cached token, do the token refresh.
  555. if (cachedToken) {
  556. // Clean up expired tokens by checking the token refresh policy.
  557. [self.installations
  558. installationIDWithCompletion:^(NSString *_Nullable identifier, NSError *_Nullable error) {
  559. if ([self.tokenManager checkTokenRefreshPolicyWithIID:identifier]) {
  560. // Default token is expired, fetch default token from server.
  561. [self defaultTokenWithHandler:nil];
  562. }
  563. // Notify FCM with the default token.
  564. self.defaultFCMToken = [self token];
  565. }];
  566. } else if ([self isFCMAutoInitEnabled]) {
  567. // When there is no cached token, must check auto init is enabled.
  568. // If it's disabled, don't initiate token generation/refresh.
  569. // If no cache token and auto init is enabled, fetch a token from server.
  570. [self defaultTokenWithHandler:nil];
  571. // Notify FCM with the default token.
  572. self.defaultFCMToken = [self token];
  573. }
  574. // ONLY checkin when auto data collection is turned on.
  575. if ([self isFCMAutoInitEnabled]) {
  576. [self.tokenManager.authService scheduleCheckin:YES];
  577. }
  578. }
  579. - (BOOL)isFCMAutoInitEnabled {
  580. Class messagingClass = NSClassFromString(kFIRInstanceIDFCMSDKClassString);
  581. // Firebase Messaging is not installed, auto init should be disabled since it's for FCM.
  582. if (!messagingClass) {
  583. return NO;
  584. }
  585. // Messaging doesn't have the class method, auto init should be enabled since FCM exists.
  586. SEL autoInitSelector = NSSelectorFromString(kFIRInstanceIDFCMSelectorAutoInitEnabled);
  587. if (![messagingClass respondsToSelector:autoInitSelector]) {
  588. return YES;
  589. }
  590. // Get the autoInitEnabled class method.
  591. IMP isAutoInitEnabledIMP = [messagingClass methodForSelector:autoInitSelector];
  592. BOOL(*isAutoInitEnabled)
  593. (Class, SEL, GULUserDefaults *) = (BOOL(*)(id, SEL, GULUserDefaults *))isAutoInitEnabledIMP;
  594. // Check FCM's isAutoInitEnabled property.
  595. return isAutoInitEnabled(messagingClass, autoInitSelector,
  596. [GULUserDefaults standardUserDefaults]);
  597. }
  598. // Actually makes InstanceID instantiate both the IID and Token-related subsystems.
  599. - (void)start {
  600. if (![FIRInstanceIDStore hasSubDirectory:kFIRInstanceIDSubDirectoryName]) {
  601. [FIRInstanceIDStore createSubDirectory:kFIRInstanceIDSubDirectoryName];
  602. }
  603. [self setupTokenManager];
  604. self.installations = [FIRInstallations installations];
  605. [self setupNotificationListeners];
  606. }
  607. // Creates the token manager, which is used for fetching, caching, and retrieving tokens.
  608. - (void)setupTokenManager {
  609. self.tokenManager = [[FIRInstanceIDTokenManager alloc] init];
  610. }
  611. - (void)setupNotificationListeners {
  612. // To prevent double notifications remove observer from all events during setup.
  613. NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  614. [center removeObserver:self];
  615. [center addObserver:self
  616. selector:@selector(notifyIdentityReset)
  617. name:kFIRInstanceIDIdentityInvalidatedNotification
  618. object:nil];
  619. [center addObserver:self
  620. selector:@selector(notifyAPNSTokenIsSet:)
  621. name:kFIRInstanceIDAPNSTokenNotification
  622. object:nil];
  623. [self observeFirebaseInstallationIDChanges];
  624. [self observeFirebaseMessagingTokenChanges];
  625. }
  626. #pragma mark - Private Helpers
  627. /// Maximum retry count to fetch the default token.
  628. + (int64_t)maxRetryCountForDefaultToken {
  629. return kMaxRetryCountForDefaultToken;
  630. }
  631. /// Minimum interval in seconds between retries to fetch the default token.
  632. + (int64_t)minIntervalForDefaultTokenRetry {
  633. return kMinRetryIntervalForDefaultTokenInSeconds;
  634. }
  635. /// Maximum retry interval between retries to fetch default token.
  636. + (int64_t)maxRetryIntervalForDefaultTokenInSeconds {
  637. return kMaxRetryIntervalForDefaultTokenInSeconds;
  638. }
  639. - (NSInteger)retryIntervalToFetchDefaultToken {
  640. if (self.retryCountForDefaultToken >= [[self class] maxRetryCountForDefaultToken]) {
  641. return (NSInteger)[[self class] maxRetryIntervalForDefaultTokenInSeconds];
  642. }
  643. // exponential backoff with a fixed initial retry time
  644. // 11s, 22s, 44s, 88s ...
  645. int64_t minInterval = [[self class] minIntervalForDefaultTokenRetry];
  646. return (NSInteger)MIN(
  647. (1 << self.retryCountForDefaultToken) + minInterval * self.retryCountForDefaultToken,
  648. kMaxRetryIntervalForDefaultTokenInSeconds);
  649. }
  650. - (void)defaultTokenWithHandler:(nullable FIRInstanceIDTokenHandler)aHandler {
  651. [self defaultTokenWithRetry:NO handler:aHandler];
  652. }
  653. /**
  654. * @param retry Indicates if the method is called to perform a retry after a failed attempt.
  655. * If `YES`, then actual token request will be performed even if `self.defaultTokenFetchHandler !=
  656. * nil`
  657. */
  658. - (void)defaultTokenWithRetry:(BOOL)retry handler:(nullable FIRInstanceIDTokenHandler)aHandler {
  659. BOOL shouldPerformRequest = retry || self.defaultTokenFetchHandler == nil;
  660. if (!self.defaultTokenFetchHandler) {
  661. self.defaultTokenFetchHandler = [[FIRInstanceIDCombinedHandler<NSString *> alloc] init];
  662. }
  663. if (aHandler) {
  664. [self.defaultTokenFetchHandler addHandler:aHandler];
  665. }
  666. if (!shouldPerformRequest) {
  667. return;
  668. }
  669. NSDictionary *instanceIDOptions = @{};
  670. BOOL hasFirebaseMessaging = NSClassFromString(kFIRInstanceIDFCMSDKClassString) != nil;
  671. if (hasFirebaseMessaging) {
  672. instanceIDOptions = [self defaultTokenOptions];
  673. }
  674. FIRInstanceID_WEAKIFY(self);
  675. FIRInstanceIDTokenHandler newHandler = ^void(NSString *token, NSError *error) {
  676. FIRInstanceID_STRONGIFY(self);
  677. if (error) {
  678. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID009,
  679. @"Failed to fetch default token %@", error);
  680. // This notification can be sent multiple times since we can't guarantee success at any point
  681. // of time.
  682. NSNotification *tokenFetchFailNotification =
  683. [NSNotification notificationWithName:kFIRInstanceIDDefaultGCMTokenFailNotification
  684. object:[error copy]];
  685. [[NSNotificationQueue defaultQueue] enqueueNotification:tokenFetchFailNotification
  686. postingStyle:NSPostASAP];
  687. self.retryCountForDefaultToken = (NSInteger)MIN(self.retryCountForDefaultToken + 1,
  688. [[self class] maxRetryCountForDefaultToken]);
  689. // Do not retry beyond the maximum limit.
  690. if (self.retryCountForDefaultToken < [[self class] maxRetryCountForDefaultToken]) {
  691. NSInteger retryInterval = [self retryIntervalToFetchDefaultToken];
  692. [self retryGetDefaultTokenAfter:retryInterval];
  693. } else {
  694. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID007,
  695. @"Failed to retrieve the default FCM token after %ld retries",
  696. (long)self.retryCountForDefaultToken);
  697. [self performDefaultTokenHandlerWithToken:nil error:error];
  698. }
  699. } else {
  700. // If somebody updated IID with APNS token while our initial request did not have it
  701. // set we need to update it on the server.
  702. NSData *deviceTokenInRequest = instanceIDOptions[kFIRInstanceIDTokenOptionsAPNSKey];
  703. BOOL isSandboxInRequest =
  704. [instanceIDOptions[kFIRInstanceIDTokenOptionsAPNSIsSandboxKey] boolValue];
  705. // Note that APNSTupleStringInRequest will be nil if deviceTokenInRequest is nil
  706. NSString *APNSTupleStringInRequest = FIRInstanceIDAPNSTupleStringForTokenAndServerType(
  707. deviceTokenInRequest, isSandboxInRequest);
  708. // If the APNs value either remained nil, or was the same non-nil value, the APNs value
  709. // did not change.
  710. BOOL APNSRemainedSameDuringFetch =
  711. (self.APNSTupleString == nil && APNSTupleStringInRequest == nil) ||
  712. ([self.APNSTupleString isEqualToString:APNSTupleStringInRequest]);
  713. if (!APNSRemainedSameDuringFetch && hasFirebaseMessaging) {
  714. // APNs value did change mid-fetch, so the token should be re-fetched with the current APNs
  715. // value.
  716. [self retryGetDefaultTokenAfter:0];
  717. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeRefetchingTokenForAPNS,
  718. @"Received APNS token while fetching default token. "
  719. @"Refetching default token.");
  720. // Do not notify and handle completion handler since this is a retry.
  721. // Simply return.
  722. return;
  723. } else {
  724. FIRInstanceIDLoggerInfo(kFIRInstanceIDMessageCodeInstanceID010,
  725. @"Successfully fetched default token.");
  726. }
  727. // Post the required notifications if somebody is waiting.
  728. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID008, @"Got default token %@",
  729. token);
  730. // Update default FCM token, this method also triggers sending notification if token has
  731. // changed.
  732. self.defaultFCMToken = token;
  733. [self performDefaultTokenHandlerWithToken:token error:nil];
  734. }
  735. };
  736. [self tokenWithAuthorizedEntity:self.fcmSenderID
  737. scope:kFIRInstanceIDDefaultTokenScope
  738. options:instanceIDOptions
  739. handler:newHandler];
  740. }
  741. /**
  742. *
  743. */
  744. - (void)performDefaultTokenHandlerWithToken:(NSString *)token error:(NSError *)error {
  745. if (!self.defaultTokenFetchHandler) {
  746. return;
  747. }
  748. [self.defaultTokenFetchHandler combinedHandler](token, error);
  749. self.defaultTokenFetchHandler = nil;
  750. }
  751. - (void)retryGetDefaultTokenAfter:(NSTimeInterval)retryInterval {
  752. FIRInstanceID_WEAKIFY(self);
  753. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(retryInterval * NSEC_PER_SEC)),
  754. dispatch_get_main_queue(), ^{
  755. FIRInstanceID_STRONGIFY(self);
  756. // Pass nil: no new handlers to be added, currently existing handlers
  757. // will be called
  758. [self defaultTokenWithRetry:YES handler:nil];
  759. });
  760. }
  761. - (BOOL)isDefaultTokenWithAuthorizedEntity:(NSString *)authorizedEntity scope:(NSString *)scope {
  762. return [authorizedEntity isEqualToString:self.fcmSenderID] &&
  763. [scope isEqualToString:kFIRInstanceIDDefaultTokenScope];
  764. }
  765. #pragma mark - APNS Token
  766. // This should only be triggered from FCM.
  767. - (void)notifyAPNSTokenIsSet:(NSNotification *)notification {
  768. NSData *token = notification.object;
  769. if (!token || ![token isKindOfClass:[NSData class]]) {
  770. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInternal002, @"Invalid APNS token type %@",
  771. NSStringFromClass([notification.object class]));
  772. return;
  773. }
  774. NSInteger type = [notification.userInfo[kFIRInstanceIDAPNSTokenType] integerValue];
  775. // The APNS token is being added, or has changed (rare)
  776. if ([self.apnsTokenData isEqualToData:token]) {
  777. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID011,
  778. @"Trying to reset APNS token to the same value. Will return");
  779. return;
  780. }
  781. // Use this token type for when we have to automatically fetch tokens in the future
  782. self.apnsTokenType = type;
  783. BOOL isSandboxApp = (type == FIRInstanceIDAPNSTokenTypeSandbox);
  784. if (self.apnsTokenType == FIRInstanceIDAPNSTokenTypeUnknown) {
  785. isSandboxApp = [self isSandboxApp];
  786. }
  787. self.apnsTokenData = [token copy];
  788. self.APNSTupleString = FIRInstanceIDAPNSTupleStringForTokenAndServerType(token, isSandboxApp);
  789. // Pro-actively invalidate the default token, if the APNs change makes it
  790. // invalid. Previously, we invalidated just before fetching the token.
  791. NSArray<FIRInstanceIDTokenInfo *> *invalidatedTokens =
  792. [self.tokenManager updateTokensToAPNSDeviceToken:self.apnsTokenData isSandbox:isSandboxApp];
  793. // Re-fetch any invalidated tokens automatically, this time with the current APNs token, so that
  794. // they are up-to-date.
  795. if (invalidatedTokens.count > 0) {
  796. FIRInstanceID_WEAKIFY(self);
  797. [self.installations
  798. installationIDWithCompletion:^(NSString *_Nullable identifier, NSError *_Nullable error) {
  799. FIRInstanceID_STRONGIFY(self);
  800. if (self == nil) {
  801. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID017,
  802. @"Instance ID shut down during token reset. Aborting");
  803. return;
  804. }
  805. if (self.apnsTokenData == nil) {
  806. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID018,
  807. @"apnsTokenData was set to nil during token reset. Aborting");
  808. return;
  809. }
  810. NSMutableDictionary *tokenOptions = [@{
  811. kFIRInstanceIDTokenOptionsAPNSKey : self.apnsTokenData,
  812. kFIRInstanceIDTokenOptionsAPNSIsSandboxKey : @(isSandboxApp)
  813. } mutableCopy];
  814. if (self.firebaseAppID) {
  815. tokenOptions[kFIRInstanceIDTokenOptionsFirebaseAppIDKey] = self.firebaseAppID;
  816. }
  817. for (FIRInstanceIDTokenInfo *tokenInfo in invalidatedTokens) {
  818. if ([tokenInfo.token isEqualToString:self.defaultFCMToken]) {
  819. // We will perform a special fetch for the default FCM token, so that the delegate
  820. // methods are called. For all others, we will do an internal re-fetch.
  821. [self defaultTokenWithHandler:nil];
  822. } else {
  823. [self.tokenManager fetchNewTokenWithAuthorizedEntity:tokenInfo.authorizedEntity
  824. scope:tokenInfo.scope
  825. instanceID:identifier
  826. options:tokenOptions
  827. handler:^(NSString *_Nullable token,
  828. NSError *_Nullable error){
  829. }];
  830. }
  831. }
  832. }];
  833. }
  834. }
  835. - (BOOL)isSandboxApp {
  836. static BOOL isSandboxApp = YES;
  837. static dispatch_once_t onceToken;
  838. dispatch_once(&onceToken, ^{
  839. isSandboxApp = ![self isProductionApp];
  840. });
  841. return isSandboxApp;
  842. }
  843. - (BOOL)isProductionApp {
  844. const BOOL defaultAppTypeProd = YES;
  845. NSError *error = nil;
  846. if ([GULAppEnvironmentUtil isSimulator]) {
  847. [self logAPNSConfigurationError:@"Running InstanceID on a simulator doesn't have APNS. "
  848. @"Use prod profile by default."];
  849. return defaultAppTypeProd;
  850. }
  851. if ([GULAppEnvironmentUtil isFromAppStore]) {
  852. // Apps distributed via AppStore or TestFlight use the Production APNS certificates.
  853. return defaultAppTypeProd;
  854. }
  855. #if TARGET_OS_OSX || TARGET_OS_MACCATALYST
  856. NSString *path = [[[[NSBundle mainBundle] resourcePath] stringByDeletingLastPathComponent]
  857. stringByAppendingPathComponent:@"embedded.provisionprofile"];
  858. #elif TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
  859. NSString *path = [[[NSBundle mainBundle] bundlePath]
  860. stringByAppendingPathComponent:@"embedded.mobileprovision"];
  861. #endif
  862. if ([GULAppEnvironmentUtil isAppStoreReceiptSandbox] && !path.length) {
  863. // Distributed via TestFlight
  864. return defaultAppTypeProd;
  865. }
  866. NSMutableData *profileData = [NSMutableData dataWithContentsOfFile:path options:0 error:&error];
  867. if (!profileData.length || error) {
  868. NSString *errorString =
  869. [NSString stringWithFormat:@"Error while reading embedded mobileprovision %@", error];
  870. [self logAPNSConfigurationError:errorString];
  871. return defaultAppTypeProd;
  872. }
  873. // The "embedded.mobileprovision" sometimes contains characters with value 0, which signals the
  874. // end of a c-string and halts the ASCII parser, or with value > 127, which violates strict 7-bit
  875. // ASCII. Replace any 0s or invalid characters in the input.
  876. uint8_t *profileBytes = (uint8_t *)profileData.bytes;
  877. for (int i = 0; i < profileData.length; i++) {
  878. uint8_t currentByte = profileBytes[i];
  879. if (!currentByte || currentByte > 127) {
  880. profileBytes[i] = '.';
  881. }
  882. }
  883. NSString *embeddedProfile = [[NSString alloc] initWithBytesNoCopy:profileBytes
  884. length:profileData.length
  885. encoding:NSASCIIStringEncoding
  886. freeWhenDone:NO];
  887. if (error || !embeddedProfile.length) {
  888. NSString *errorString =
  889. [NSString stringWithFormat:@"Error while reading embedded mobileprovision %@", error];
  890. [self logAPNSConfigurationError:errorString];
  891. return defaultAppTypeProd;
  892. }
  893. NSScanner *scanner = [NSScanner scannerWithString:embeddedProfile];
  894. NSString *plistContents;
  895. if ([scanner scanUpToString:@"<plist" intoString:nil]) {
  896. if ([scanner scanUpToString:@"</plist>" intoString:&plistContents]) {
  897. plistContents = [plistContents stringByAppendingString:@"</plist>"];
  898. }
  899. }
  900. if (!plistContents.length) {
  901. return defaultAppTypeProd;
  902. }
  903. NSData *data = [plistContents dataUsingEncoding:NSUTF8StringEncoding];
  904. if (!data.length) {
  905. [self logAPNSConfigurationError:@"Couldn't read plist fetched from embedded mobileprovision"];
  906. return defaultAppTypeProd;
  907. }
  908. NSError *plistMapError;
  909. id plistData = [NSPropertyListSerialization propertyListWithData:data
  910. options:NSPropertyListImmutable
  911. format:nil
  912. error:&plistMapError];
  913. if (plistMapError || ![plistData isKindOfClass:[NSDictionary class]]) {
  914. NSString *errorString =
  915. [NSString stringWithFormat:@"Error while converting assumed plist to dict %@",
  916. plistMapError.localizedDescription];
  917. [self logAPNSConfigurationError:errorString];
  918. return defaultAppTypeProd;
  919. }
  920. NSDictionary *plistMap = (NSDictionary *)plistData;
  921. if ([plistMap valueForKeyPath:@"ProvisionedDevices"]) {
  922. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID012,
  923. @"Provisioning profile has specifically provisioned devices, "
  924. @"most likely a Dev profile.");
  925. }
  926. NSString *apsEnvironment = [plistMap valueForKeyPath:kEntitlementsAPSEnvironmentKey];
  927. NSString *debugString __unused =
  928. [NSString stringWithFormat:@"APNS Environment in profile: %@", apsEnvironment];
  929. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID013, @"%@", debugString);
  930. // No aps-environment in the profile.
  931. if (!apsEnvironment.length) {
  932. [self logAPNSConfigurationError:@"No aps-environment set. If testing on a device APNS is not "
  933. @"correctly configured. Please recheck your provisioning "
  934. @"profiles. If testing on a simulator this is fine since APNS "
  935. @"doesn't work on the simulator."];
  936. return defaultAppTypeProd;
  937. }
  938. if ([apsEnvironment isEqualToString:kAPSEnvironmentDevelopmentValue]) {
  939. return NO;
  940. }
  941. return defaultAppTypeProd;
  942. }
  943. /// Log error messages only when Messaging exists in the pod.
  944. - (void)logAPNSConfigurationError:(NSString *)errorString {
  945. BOOL hasFirebaseMessaging = NSClassFromString(kFIRInstanceIDFCMSDKClassString) != nil;
  946. if (hasFirebaseMessaging) {
  947. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID014, @"%@", errorString);
  948. } else {
  949. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID015, @"%@", errorString);
  950. }
  951. }
  952. #pragma mark - Sync InstanceID
  953. - (void)updateFirebaseInstallationID {
  954. FIRInstanceID_WEAKIFY(self);
  955. [self.installations
  956. installationIDWithCompletion:^(NSString *_Nullable installationID, NSError *_Nullable error) {
  957. FIRInstanceID_STRONGIFY(self);
  958. self.firebaseInstallationsID = installationID;
  959. }];
  960. }
  961. - (void)installationIDDidChangeNotificationReceived:(NSNotification *)notification {
  962. NSString *installationAppID =
  963. notification.userInfo[kFIRInstallationIDDidChangeNotificationAppNameKey];
  964. if ([installationAppID isKindOfClass:[NSString class]] &&
  965. [installationAppID isEqual:self.firebaseAppID]) {
  966. [self updateFirebaseInstallationID];
  967. }
  968. }
  969. - (void)observeFirebaseInstallationIDChanges {
  970. [[NSNotificationCenter defaultCenter] removeObserver:self
  971. name:FIRInstallationIDDidChangeNotification
  972. object:nil];
  973. [[NSNotificationCenter defaultCenter]
  974. addObserver:self
  975. selector:@selector(installationIDDidChangeNotificationReceived:)
  976. name:FIRInstallationIDDidChangeNotification
  977. object:nil];
  978. }
  979. - (void)observeFirebaseMessagingTokenChanges {
  980. [[NSNotificationCenter defaultCenter]
  981. removeObserver:self
  982. name:kFIRInstanceIDMessagingUpdateTokenNotification
  983. object:nil];
  984. [[NSNotificationCenter defaultCenter]
  985. addObserver:self
  986. selector:@selector(messagingTokenDidChangeNotificationReceived:)
  987. name:kFIRInstanceIDMessagingUpdateTokenNotification
  988. object:nil];
  989. }
  990. - (void)messagingTokenDidChangeNotificationReceived:(NSNotification *)notification {
  991. NSString *tokenUpdatedFromMessaging = notification.object;
  992. if (!tokenUpdatedFromMessaging || [tokenUpdatedFromMessaging isKindOfClass:[NSString class]]) {
  993. self.defaultFCMToken = tokenUpdatedFromMessaging;
  994. [self.tokenManager saveDefaultToken:tokenUpdatedFromMessaging
  995. withOptions:[self defaultTokenOptions]];
  996. }
  997. }
  998. - (NSDictionary *)defaultTokenOptions {
  999. NSDictionary *tokenOptions = @{};
  1000. if (self.apnsTokenData) {
  1001. BOOL isSandboxApp = (self.apnsTokenType == FIRInstanceIDAPNSTokenTypeSandbox);
  1002. if (self.apnsTokenType == FIRInstanceIDAPNSTokenTypeUnknown) {
  1003. isSandboxApp = [self isSandboxApp];
  1004. }
  1005. tokenOptions = @{
  1006. kFIRInstanceIDTokenOptionsAPNSKey : self.apnsTokenData,
  1007. kFIRInstanceIDTokenOptionsAPNSIsSandboxKey : @(isSandboxApp),
  1008. };
  1009. }
  1010. return tokenOptions;
  1011. }
  1012. @end