FIRInstanceID.m 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
  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. dispatch_async(dispatch_get_main_queue(), ^{
  264. handler(token, error);
  265. });
  266. };
  267. if (errorCode != noError) {
  268. newHandler(nil, [NSError errorWithFIRInstanceIDErrorCode:errorCode]);
  269. return;
  270. }
  271. FIRInstanceID_WEAKIFY(self);
  272. FIRInstanceIDAuthService *authService = self.tokenManager.authService;
  273. [authService fetchCheckinInfoWithHandler:^(FIRInstanceIDCheckinPreferences *preferences,
  274. NSError *error) {
  275. FIRInstanceID_STRONGIFY(self);
  276. if (error) {
  277. newHandler(nil, error);
  278. return;
  279. }
  280. FIRInstanceID_WEAKIFY(self);
  281. [self.installations installationIDWithCompletion:^(NSString *_Nullable identifier,
  282. NSError *_Nullable error) {
  283. FIRInstanceID_STRONGIFY(self);
  284. if (error) {
  285. NSError *newError =
  286. [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidKeyPair];
  287. newHandler(nil, newError);
  288. } else {
  289. FIRInstanceIDTokenInfo *cachedTokenInfo =
  290. [self.tokenManager cachedTokenInfoWithAuthorizedEntity:authorizedEntity scope:scope];
  291. if (cachedTokenInfo) {
  292. FIRInstanceIDAPNSInfo *optionsAPNSInfo =
  293. [[FIRInstanceIDAPNSInfo alloc] initWithTokenOptionsDictionary:tokenOptions];
  294. // Check if APNS Info is changed
  295. if ((!cachedTokenInfo.APNSInfo && !optionsAPNSInfo) ||
  296. [cachedTokenInfo.APNSInfo isEqualToAPNSInfo:optionsAPNSInfo]) {
  297. // check if token is fresh
  298. if ([cachedTokenInfo isFreshWithIID:identifier]) {
  299. newHandler(cachedTokenInfo.token, nil);
  300. return;
  301. }
  302. }
  303. }
  304. [self.tokenManager fetchNewTokenWithAuthorizedEntity:[authorizedEntity copy]
  305. scope:[scope copy]
  306. instanceID:identifier
  307. options:tokenOptions
  308. handler:newHandler];
  309. }
  310. }];
  311. }];
  312. }
  313. - (void)deleteTokenWithAuthorizedEntity:(NSString *)authorizedEntity
  314. scope:(NSString *)scope
  315. handler:(FIRInstanceIDDeleteTokenHandler)handler {
  316. if (!handler) {
  317. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID001,
  318. kFIRInstanceIDInvalidNilHandlerError);
  319. return;
  320. }
  321. // comparing enums to ints directly throws a warning
  322. FIRInstanceIDErrorCode noError = INT_MAX;
  323. FIRInstanceIDErrorCode errorCode = noError;
  324. if (![authorizedEntity length]) {
  325. errorCode = kFIRInstanceIDErrorCodeInvalidAuthorizedEntity;
  326. } else if (![scope length]) {
  327. errorCode = kFIRInstanceIDErrorCodeInvalidScope;
  328. } else if (!self.installations) {
  329. errorCode = kFIRInstanceIDErrorCodeInvalidStart;
  330. }
  331. FIRInstanceIDDeleteTokenHandler newHandler = ^(NSError *error) {
  332. // If a default token is deleted successfully, reset the defaultFCMToken too.
  333. if (!error && [authorizedEntity isEqualToString:self.fcmSenderID] &&
  334. [scope isEqualToString:kFIRInstanceIDDefaultTokenScope]) {
  335. self.defaultFCMToken = nil;
  336. }
  337. dispatch_async(dispatch_get_main_queue(), ^{
  338. handler(error);
  339. });
  340. };
  341. if (errorCode != noError) {
  342. newHandler([NSError errorWithFIRInstanceIDErrorCode:errorCode]);
  343. return;
  344. }
  345. FIRInstanceID_WEAKIFY(self);
  346. FIRInstanceIDAuthService *authService = self.tokenManager.authService;
  347. [authService
  348. fetchCheckinInfoWithHandler:^(FIRInstanceIDCheckinPreferences *preferences, NSError *error) {
  349. FIRInstanceID_STRONGIFY(self);
  350. if (error) {
  351. newHandler(error);
  352. return;
  353. }
  354. FIRInstanceID_WEAKIFY(self);
  355. [self.installations installationIDWithCompletion:^(NSString *_Nullable identifier,
  356. NSError *_Nullable error) {
  357. FIRInstanceID_STRONGIFY(self);
  358. if (error) {
  359. NSError *newError =
  360. [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidKeyPair];
  361. newHandler(newError);
  362. } else {
  363. [self.tokenManager deleteTokenWithAuthorizedEntity:authorizedEntity
  364. scope:scope
  365. instanceID:identifier
  366. handler:newHandler];
  367. }
  368. }];
  369. }];
  370. }
  371. #pragma mark - Identity
  372. - (void)getIDWithHandler:(FIRInstanceIDHandler)handler {
  373. if (!handler) {
  374. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID003,
  375. kFIRInstanceIDInvalidNilHandlerError);
  376. return;
  377. }
  378. FIRInstanceID_WEAKIFY(self);
  379. [self.installations
  380. installationIDWithCompletion:^(NSString *_Nullable identifier, NSError *_Nullable error) {
  381. FIRInstanceID_STRONGIFY(self);
  382. // When getID is explicitly called, trigger getToken to make sure token always exists.
  383. // This is to avoid ID conflict (ID is not checked for conflict until we generate a token)
  384. if (identifier) {
  385. [self token];
  386. }
  387. handler(identifier, error);
  388. }];
  389. }
  390. - (void)deleteIDWithHandler:(FIRInstanceIDDeleteHandler)handler {
  391. if (!handler) {
  392. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID004,
  393. kFIRInstanceIDInvalidNilHandlerError);
  394. return;
  395. }
  396. void (^callHandlerOnMainThread)(NSError *) = ^(NSError *error) {
  397. if ([NSThread isMainThread]) {
  398. handler(error);
  399. return;
  400. }
  401. dispatch_async(dispatch_get_main_queue(), ^{
  402. handler(error);
  403. });
  404. };
  405. if (!self.installations) {
  406. FIRInstanceIDErrorCode error = kFIRInstanceIDErrorCodeInvalidStart;
  407. callHandlerOnMainThread([NSError errorWithFIRInstanceIDErrorCode:error]);
  408. return;
  409. }
  410. FIRInstanceID_WEAKIFY(self);
  411. void (^deleteTokensHandler)(NSError *) = ^void(NSError *error) {
  412. FIRInstanceID_STRONGIFY(self);
  413. if (error) {
  414. callHandlerOnMainThread(error);
  415. return;
  416. }
  417. [self deleteIdentityWithHandler:^(NSError *error) {
  418. callHandlerOnMainThread(error);
  419. }];
  420. };
  421. [self.installations
  422. installationIDWithCompletion:^(NSString *_Nullable identifier, NSError *_Nullable error) {
  423. FIRInstanceID_STRONGIFY(self);
  424. if (error) {
  425. NSError *newError =
  426. [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidKeyPair];
  427. callHandlerOnMainThread(newError);
  428. } else {
  429. [self.tokenManager deleteAllTokensWithInstanceID:identifier handler:deleteTokensHandler];
  430. }
  431. }];
  432. }
  433. - (void)notifyIdentityReset {
  434. [self deleteIdentityWithHandler:nil];
  435. }
  436. // Delete all the local cache checkin, IID and token.
  437. - (void)deleteIdentityWithHandler:(FIRInstanceIDDeleteHandler)handler {
  438. // Delete tokens.
  439. [self.tokenManager deleteAllTokensLocallyWithHandler:^(NSError *deleteTokenError) {
  440. // Reset FCM token.
  441. self.defaultFCMToken = nil;
  442. if (deleteTokenError) {
  443. if (handler) {
  444. handler(deleteTokenError);
  445. }
  446. return;
  447. }
  448. // Delete Instance ID.
  449. [self.installations deleteWithCompletion:^(NSError *_Nullable error) {
  450. if (error) {
  451. if (handler) {
  452. handler(error);
  453. }
  454. return;
  455. }
  456. [self.tokenManager.authService resetCheckinWithHandler:^(NSError *error) {
  457. if (error) {
  458. if (handler) {
  459. handler(error);
  460. }
  461. return;
  462. }
  463. // Only request new token if FCM auto initialization is
  464. // enabled.
  465. if ([self isFCMAutoInitEnabled]) {
  466. // Deletion succeeds! Requesting new checkin, IID and token.
  467. // TODO(chliangGoogle) see if dispatch_after is necessary
  468. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)),
  469. dispatch_get_main_queue(), ^{
  470. [self defaultTokenWithHandler:nil];
  471. });
  472. }
  473. if (handler) {
  474. handler(nil);
  475. }
  476. }];
  477. }];
  478. }];
  479. }
  480. #pragma mark - Checkin
  481. - (BOOL)tryToLoadValidCheckinInfo {
  482. FIRInstanceIDCheckinPreferences *checkinPreferences =
  483. [self.tokenManager.authService checkinPreferences];
  484. return [checkinPreferences hasValidCheckinInfo];
  485. }
  486. - (NSString *)deviceAuthID {
  487. return [self.tokenManager.authService checkinPreferences].deviceID;
  488. }
  489. - (NSString *)secretToken {
  490. return [self.tokenManager.authService checkinPreferences].secretToken;
  491. }
  492. - (NSString *)versionInfo {
  493. return [self.tokenManager.authService checkinPreferences].versionInfo;
  494. }
  495. #pragma mark - Config
  496. + (void)load {
  497. [FIRApp registerInternalLibrary:(Class<FIRLibrary>)self
  498. withName:@"fire-iid"
  499. withVersion:FIRInstanceIDCurrentLibraryVersion()];
  500. }
  501. + (nonnull NSArray<FIRComponent *> *)componentsToRegister {
  502. FIRComponentCreationBlock creationBlock =
  503. ^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) {
  504. // InstanceID only works with the default app.
  505. if (!container.app.isDefaultApp) {
  506. // Only configure for the default FIRApp.
  507. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeFIRApp002,
  508. @"Firebase Instance ID only works with the default app.");
  509. return nil;
  510. }
  511. // Ensure it's cached so it returns the same instance every time instanceID is called.
  512. *isCacheable = YES;
  513. FIRInstanceID *instanceID = [[FIRInstanceID alloc] initPrivately];
  514. [instanceID start];
  515. [instanceID configureInstanceIDWithOptions:container.app.options];
  516. return instanceID;
  517. };
  518. FIRComponent *instanceIDProvider =
  519. [FIRComponent componentWithProtocol:@protocol(FIRInstanceIDInstanceProvider)
  520. instantiationTiming:FIRInstantiationTimingEagerInDefaultApp
  521. dependencies:@[]
  522. creationBlock:creationBlock];
  523. return @[ instanceIDProvider ];
  524. }
  525. - (void)configureInstanceIDWithOptions:(FIROptions *)options {
  526. NSString *GCMSenderID = options.GCMSenderID;
  527. if (!GCMSenderID.length) {
  528. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeFIRApp000,
  529. @"Firebase not set up correctly, nil or empty senderID.");
  530. [NSException raise:kFIRIIDErrorDomain
  531. format:@"Could not configure Firebase InstanceID. GCMSenderID must not be nil or "
  532. @"empty."];
  533. }
  534. self.fcmSenderID = GCMSenderID;
  535. self.firebaseAppID = options.googleAppID;
  536. [self updateFirebaseInstallationID];
  537. // FCM generates a FCM token during app start for sending push notification to device.
  538. // This is not needed for app extension except for watch.
  539. #if TARGET_OS_WATCH
  540. [self didCompleteConfigure];
  541. #else
  542. if (![GULAppEnvironmentUtil isAppExtension]) {
  543. [self didCompleteConfigure];
  544. }
  545. #endif
  546. }
  547. // This is used to start any operations when we receive FirebaseSDK setup notification
  548. // from FIRCore.
  549. - (void)didCompleteConfigure {
  550. NSString *cachedToken = [self cachedTokenIfAvailable];
  551. // When there is a cached token, do the token refresh.
  552. if (cachedToken) {
  553. // Clean up expired tokens by checking the token refresh policy.
  554. [self.installations
  555. installationIDWithCompletion:^(NSString *_Nullable identifier, NSError *_Nullable error) {
  556. if ([self.tokenManager checkTokenRefreshPolicyWithIID:identifier]) {
  557. // Default token is expired, fetch default token from server.
  558. [self defaultTokenWithHandler:nil];
  559. }
  560. // Notify FCM with the default token.
  561. self.defaultFCMToken = [self token];
  562. }];
  563. } else if ([self isFCMAutoInitEnabled]) {
  564. // When there is no cached token, must check auto init is enabled.
  565. // If it's disabled, don't initiate token generation/refresh.
  566. // If no cache token and auto init is enabled, fetch a token from server.
  567. [self defaultTokenWithHandler:nil];
  568. // Notify FCM with the default token.
  569. self.defaultFCMToken = [self token];
  570. }
  571. // ONLY checkin when auto data collection is turned on.
  572. if ([self isFCMAutoInitEnabled]) {
  573. [self.tokenManager.authService scheduleCheckin:YES];
  574. }
  575. }
  576. - (BOOL)isFCMAutoInitEnabled {
  577. Class messagingClass = NSClassFromString(kFIRInstanceIDFCMSDKClassString);
  578. // Firebase Messaging is not installed, auto init should be disabled since it's for FCM.
  579. if (!messagingClass) {
  580. return NO;
  581. }
  582. // Messaging doesn't have the class method, auto init should be enabled since FCM exists.
  583. SEL autoInitSelector = NSSelectorFromString(kFIRInstanceIDFCMSelectorAutoInitEnabled);
  584. if (![messagingClass respondsToSelector:autoInitSelector]) {
  585. return YES;
  586. }
  587. // Get the autoInitEnabled class method.
  588. IMP isAutoInitEnabledIMP = [messagingClass methodForSelector:autoInitSelector];
  589. BOOL(*isAutoInitEnabled)
  590. (Class, SEL, GULUserDefaults *) = (BOOL(*)(id, SEL, GULUserDefaults *))isAutoInitEnabledIMP;
  591. // Check FCM's isAutoInitEnabled property.
  592. return isAutoInitEnabled(messagingClass, autoInitSelector,
  593. [GULUserDefaults standardUserDefaults]);
  594. }
  595. // Actually makes InstanceID instantiate both the IID and Token-related subsystems.
  596. - (void)start {
  597. if (![FIRInstanceIDStore hasSubDirectory:kFIRInstanceIDSubDirectoryName]) {
  598. [FIRInstanceIDStore createSubDirectory:kFIRInstanceIDSubDirectoryName];
  599. }
  600. [self setupTokenManager];
  601. self.installations = [FIRInstallations installations];
  602. [self setupNotificationListeners];
  603. }
  604. // Creates the token manager, which is used for fetching, caching, and retrieving tokens.
  605. - (void)setupTokenManager {
  606. self.tokenManager = [[FIRInstanceIDTokenManager alloc] init];
  607. }
  608. - (void)setupNotificationListeners {
  609. // To prevent double notifications remove observer from all events during setup.
  610. NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  611. [center removeObserver:self];
  612. [center addObserver:self
  613. selector:@selector(notifyIdentityReset)
  614. name:kFIRInstanceIDIdentityInvalidatedNotification
  615. object:nil];
  616. [center addObserver:self
  617. selector:@selector(notifyAPNSTokenIsSet:)
  618. name:kFIRInstanceIDAPNSTokenNotification
  619. object:nil];
  620. [self observeFirebaseInstallationIDChanges];
  621. }
  622. #pragma mark - Private Helpers
  623. /// Maximum retry count to fetch the default token.
  624. + (int64_t)maxRetryCountForDefaultToken {
  625. return kMaxRetryCountForDefaultToken;
  626. }
  627. /// Minimum interval in seconds between retries to fetch the default token.
  628. + (int64_t)minIntervalForDefaultTokenRetry {
  629. return kMinRetryIntervalForDefaultTokenInSeconds;
  630. }
  631. /// Maximum retry interval between retries to fetch default token.
  632. + (int64_t)maxRetryIntervalForDefaultTokenInSeconds {
  633. return kMaxRetryIntervalForDefaultTokenInSeconds;
  634. }
  635. - (NSInteger)retryIntervalToFetchDefaultToken {
  636. if (self.retryCountForDefaultToken >= [[self class] maxRetryCountForDefaultToken]) {
  637. return (NSInteger)[[self class] maxRetryIntervalForDefaultTokenInSeconds];
  638. }
  639. // exponential backoff with a fixed initial retry time
  640. // 11s, 22s, 44s, 88s ...
  641. int64_t minInterval = [[self class] minIntervalForDefaultTokenRetry];
  642. return (NSInteger)MIN(
  643. (1 << self.retryCountForDefaultToken) + minInterval * self.retryCountForDefaultToken,
  644. kMaxRetryIntervalForDefaultTokenInSeconds);
  645. }
  646. - (void)defaultTokenWithHandler:(nullable FIRInstanceIDTokenHandler)aHandler {
  647. [self defaultTokenWithRetry:NO handler:aHandler];
  648. }
  649. /**
  650. * @param retry Indicates if the method is called to perform a retry after a failed attempt.
  651. * If `YES`, then actual token request will be performed even if `self.defaultTokenFetchHandler !=
  652. * nil`
  653. */
  654. - (void)defaultTokenWithRetry:(BOOL)retry handler:(nullable FIRInstanceIDTokenHandler)aHandler {
  655. BOOL shouldPerformRequest = retry || self.defaultTokenFetchHandler == nil;
  656. if (!self.defaultTokenFetchHandler) {
  657. self.defaultTokenFetchHandler = [[FIRInstanceIDCombinedHandler<NSString *> alloc] init];
  658. }
  659. if (aHandler) {
  660. [self.defaultTokenFetchHandler addHandler:aHandler];
  661. }
  662. if (!shouldPerformRequest) {
  663. return;
  664. }
  665. NSDictionary *instanceIDOptions = @{};
  666. BOOL hasFirebaseMessaging = NSClassFromString(kFIRInstanceIDFCMSDKClassString) != nil;
  667. if (hasFirebaseMessaging && self.apnsTokenData) {
  668. BOOL isSandboxApp = (self.apnsTokenType == FIRInstanceIDAPNSTokenTypeSandbox);
  669. if (self.apnsTokenType == FIRInstanceIDAPNSTokenTypeUnknown) {
  670. isSandboxApp = [self isSandboxApp];
  671. }
  672. instanceIDOptions = @{
  673. kFIRInstanceIDTokenOptionsAPNSKey : self.apnsTokenData,
  674. kFIRInstanceIDTokenOptionsAPNSIsSandboxKey : @(isSandboxApp),
  675. };
  676. }
  677. FIRInstanceID_WEAKIFY(self);
  678. FIRInstanceIDTokenHandler newHandler = ^void(NSString *token, NSError *error) {
  679. FIRInstanceID_STRONGIFY(self);
  680. if (error) {
  681. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID009,
  682. @"Failed to fetch default token %@", error);
  683. // This notification can be sent multiple times since we can't guarantee success at any point
  684. // of time.
  685. NSNotification *tokenFetchFailNotification =
  686. [NSNotification notificationWithName:kFIRInstanceIDDefaultGCMTokenFailNotification
  687. object:[error copy]];
  688. [[NSNotificationQueue defaultQueue] enqueueNotification:tokenFetchFailNotification
  689. postingStyle:NSPostASAP];
  690. self.retryCountForDefaultToken = (NSInteger)MIN(self.retryCountForDefaultToken + 1,
  691. [[self class] maxRetryCountForDefaultToken]);
  692. // Do not retry beyond the maximum limit.
  693. if (self.retryCountForDefaultToken < [[self class] maxRetryCountForDefaultToken]) {
  694. NSInteger retryInterval = [self retryIntervalToFetchDefaultToken];
  695. [self retryGetDefaultTokenAfter:retryInterval];
  696. } else {
  697. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID007,
  698. @"Failed to retrieve the default FCM token after %ld retries",
  699. (long)self.retryCountForDefaultToken);
  700. [self performDefaultTokenHandlerWithToken:nil error:error];
  701. }
  702. } else {
  703. // If somebody updated IID with APNS token while our initial request did not have it
  704. // set we need to update it on the server.
  705. NSData *deviceTokenInRequest = instanceIDOptions[kFIRInstanceIDTokenOptionsAPNSKey];
  706. BOOL isSandboxInRequest =
  707. [instanceIDOptions[kFIRInstanceIDTokenOptionsAPNSIsSandboxKey] boolValue];
  708. // Note that APNSTupleStringInRequest will be nil if deviceTokenInRequest is nil
  709. NSString *APNSTupleStringInRequest = FIRInstanceIDAPNSTupleStringForTokenAndServerType(
  710. deviceTokenInRequest, isSandboxInRequest);
  711. // If the APNs value either remained nil, or was the same non-nil value, the APNs value
  712. // did not change.
  713. BOOL APNSRemainedSameDuringFetch =
  714. (self.APNSTupleString == nil && APNSTupleStringInRequest == nil) ||
  715. ([self.APNSTupleString isEqualToString:APNSTupleStringInRequest]);
  716. if (!APNSRemainedSameDuringFetch && hasFirebaseMessaging) {
  717. // APNs value did change mid-fetch, so the token should be re-fetched with the current APNs
  718. // value.
  719. [self retryGetDefaultTokenAfter:0];
  720. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeRefetchingTokenForAPNS,
  721. @"Received APNS token while fetching default token. "
  722. @"Refetching default token.");
  723. // Do not notify and handle completion handler since this is a retry.
  724. // Simply return.
  725. return;
  726. } else {
  727. FIRInstanceIDLoggerInfo(kFIRInstanceIDMessageCodeInstanceID010,
  728. @"Successfully fetched default token.");
  729. }
  730. // Post the required notifications if somebody is waiting.
  731. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID008, @"Got default token %@",
  732. token);
  733. // Update default FCM token, this method also triggers sending notification if token has
  734. // changed.
  735. self.defaultFCMToken = token;
  736. [self performDefaultTokenHandlerWithToken:token error:nil];
  737. }
  738. };
  739. [self tokenWithAuthorizedEntity:self.fcmSenderID
  740. scope:kFIRInstanceIDDefaultTokenScope
  741. options:instanceIDOptions
  742. handler:newHandler];
  743. }
  744. /**
  745. *
  746. */
  747. - (void)performDefaultTokenHandlerWithToken:(NSString *)token error:(NSError *)error {
  748. if (!self.defaultTokenFetchHandler) {
  749. return;
  750. }
  751. [self.defaultTokenFetchHandler combinedHandler](token, error);
  752. self.defaultTokenFetchHandler = nil;
  753. }
  754. - (void)retryGetDefaultTokenAfter:(NSTimeInterval)retryInterval {
  755. FIRInstanceID_WEAKIFY(self);
  756. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(retryInterval * NSEC_PER_SEC)),
  757. dispatch_get_main_queue(), ^{
  758. FIRInstanceID_STRONGIFY(self);
  759. // Pass nil: no new handlers to be added, currently existing handlers
  760. // will be called
  761. [self defaultTokenWithRetry:YES handler:nil];
  762. });
  763. }
  764. #pragma mark - APNS Token
  765. // This should only be triggered from FCM.
  766. - (void)notifyAPNSTokenIsSet:(NSNotification *)notification {
  767. NSData *token = notification.object;
  768. if (!token || ![token isKindOfClass:[NSData class]]) {
  769. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInternal002, @"Invalid APNS token type %@",
  770. NSStringFromClass([notification.object class]));
  771. return;
  772. }
  773. NSInteger type = [notification.userInfo[kFIRInstanceIDAPNSTokenType] integerValue];
  774. // The APNS token is being added, or has changed (rare)
  775. if ([self.apnsTokenData isEqualToData:token]) {
  776. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID011,
  777. @"Trying to reset APNS token to the same value. Will return");
  778. return;
  779. }
  780. // Use this token type for when we have to automatically fetch tokens in the future
  781. self.apnsTokenType = type;
  782. BOOL isSandboxApp = (type == FIRInstanceIDAPNSTokenTypeSandbox);
  783. if (self.apnsTokenType == FIRInstanceIDAPNSTokenTypeUnknown) {
  784. isSandboxApp = [self isSandboxApp];
  785. }
  786. self.apnsTokenData = [token copy];
  787. self.APNSTupleString = FIRInstanceIDAPNSTupleStringForTokenAndServerType(token, isSandboxApp);
  788. // Pro-actively invalidate the default token, if the APNs change makes it
  789. // invalid. Previously, we invalidated just before fetching the token.
  790. NSArray<FIRInstanceIDTokenInfo *> *invalidatedTokens =
  791. [self.tokenManager updateTokensToAPNSDeviceToken:self.apnsTokenData isSandbox:isSandboxApp];
  792. // Re-fetch any invalidated tokens automatically, this time with the current APNs token, so that
  793. // they are up-to-date.
  794. if (invalidatedTokens.count > 0) {
  795. FIRInstanceID_WEAKIFY(self);
  796. [self.installations
  797. installationIDWithCompletion:^(NSString *_Nullable identifier, NSError *_Nullable error) {
  798. FIRInstanceID_STRONGIFY(self);
  799. if (self == nil) {
  800. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID017,
  801. @"Instance ID shut down during token reset. Aborting");
  802. return;
  803. }
  804. if (self.apnsTokenData == nil) {
  805. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID018,
  806. @"apnsTokenData was set to nil during token reset. Aborting");
  807. return;
  808. }
  809. NSMutableDictionary *tokenOptions = [@{
  810. kFIRInstanceIDTokenOptionsAPNSKey : self.apnsTokenData,
  811. kFIRInstanceIDTokenOptionsAPNSIsSandboxKey : @(isSandboxApp)
  812. } mutableCopy];
  813. if (self.firebaseAppID) {
  814. tokenOptions[kFIRInstanceIDTokenOptionsFirebaseAppIDKey] = self.firebaseAppID;
  815. }
  816. for (FIRInstanceIDTokenInfo *tokenInfo in invalidatedTokens) {
  817. if ([tokenInfo.token isEqualToString:self.defaultFCMToken]) {
  818. // We will perform a special fetch for the default FCM token, so that the delegate
  819. // methods are called. For all others, we will do an internal re-fetch.
  820. [self defaultTokenWithHandler:nil];
  821. } else {
  822. [self.tokenManager fetchNewTokenWithAuthorizedEntity:tokenInfo.authorizedEntity
  823. scope:tokenInfo.scope
  824. instanceID:identifier
  825. options:tokenOptions
  826. handler:^(NSString *_Nullable token,
  827. NSError *_Nullable error){
  828. }];
  829. }
  830. }
  831. }];
  832. }
  833. }
  834. - (BOOL)isSandboxApp {
  835. static BOOL isSandboxApp = YES;
  836. static dispatch_once_t onceToken;
  837. dispatch_once(&onceToken, ^{
  838. isSandboxApp = ![self isProductionApp];
  839. });
  840. return isSandboxApp;
  841. }
  842. - (BOOL)isProductionApp {
  843. const BOOL defaultAppTypeProd = YES;
  844. NSError *error = nil;
  845. if ([GULAppEnvironmentUtil isSimulator]) {
  846. [self logAPNSConfigurationError:@"Running InstanceID on a simulator doesn't have APNS. "
  847. @"Use prod profile by default."];
  848. return defaultAppTypeProd;
  849. }
  850. if ([GULAppEnvironmentUtil isFromAppStore]) {
  851. // Apps distributed via AppStore or TestFlight use the Production APNS certificates.
  852. return defaultAppTypeProd;
  853. }
  854. #if TARGET_OS_OSX || TARGET_OS_MACCATALYST
  855. NSString *path = [[[[NSBundle mainBundle] resourcePath] stringByDeletingLastPathComponent]
  856. stringByAppendingPathComponent:@"embedded.provisionprofile"];
  857. #elif TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
  858. NSString *path = [[[NSBundle mainBundle] bundlePath]
  859. stringByAppendingPathComponent:@"embedded.mobileprovision"];
  860. #endif
  861. if ([GULAppEnvironmentUtil isAppStoreReceiptSandbox] && !path.length) {
  862. // Distributed via TestFlight
  863. return defaultAppTypeProd;
  864. }
  865. NSMutableData *profileData = [NSMutableData dataWithContentsOfFile:path options:0 error:&error];
  866. if (!profileData.length || error) {
  867. NSString *errorString =
  868. [NSString stringWithFormat:@"Error while reading embedded mobileprovision %@", error];
  869. [self logAPNSConfigurationError:errorString];
  870. return defaultAppTypeProd;
  871. }
  872. // The "embedded.mobileprovision" sometimes contains characters with value 0, which signals the
  873. // end of a c-string and halts the ASCII parser, or with value > 127, which violates strict 7-bit
  874. // ASCII. Replace any 0s or invalid characters in the input.
  875. uint8_t *profileBytes = (uint8_t *)profileData.bytes;
  876. for (int i = 0; i < profileData.length; i++) {
  877. uint8_t currentByte = profileBytes[i];
  878. if (!currentByte || currentByte > 127) {
  879. profileBytes[i] = '.';
  880. }
  881. }
  882. NSString *embeddedProfile = [[NSString alloc] initWithBytesNoCopy:profileBytes
  883. length:profileData.length
  884. encoding:NSASCIIStringEncoding
  885. freeWhenDone:NO];
  886. if (error || !embeddedProfile.length) {
  887. NSString *errorString =
  888. [NSString stringWithFormat:@"Error while reading embedded mobileprovision %@", error];
  889. [self logAPNSConfigurationError:errorString];
  890. return defaultAppTypeProd;
  891. }
  892. NSScanner *scanner = [NSScanner scannerWithString:embeddedProfile];
  893. NSString *plistContents;
  894. if ([scanner scanUpToString:@"<plist" intoString:nil]) {
  895. if ([scanner scanUpToString:@"</plist>" intoString:&plistContents]) {
  896. plistContents = [plistContents stringByAppendingString:@"</plist>"];
  897. }
  898. }
  899. if (!plistContents.length) {
  900. return defaultAppTypeProd;
  901. }
  902. NSData *data = [plistContents dataUsingEncoding:NSUTF8StringEncoding];
  903. if (!data.length) {
  904. [self logAPNSConfigurationError:@"Couldn't read plist fetched from embedded mobileprovision"];
  905. return defaultAppTypeProd;
  906. }
  907. NSError *plistMapError;
  908. id plistData = [NSPropertyListSerialization propertyListWithData:data
  909. options:NSPropertyListImmutable
  910. format:nil
  911. error:&plistMapError];
  912. if (plistMapError || ![plistData isKindOfClass:[NSDictionary class]]) {
  913. NSString *errorString =
  914. [NSString stringWithFormat:@"Error while converting assumed plist to dict %@",
  915. plistMapError.localizedDescription];
  916. [self logAPNSConfigurationError:errorString];
  917. return defaultAppTypeProd;
  918. }
  919. NSDictionary *plistMap = (NSDictionary *)plistData;
  920. if ([plistMap valueForKeyPath:@"ProvisionedDevices"]) {
  921. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID012,
  922. @"Provisioning profile has specifically provisioned devices, "
  923. @"most likely a Dev profile.");
  924. }
  925. NSString *apsEnvironment = [plistMap valueForKeyPath:kEntitlementsAPSEnvironmentKey];
  926. NSString *debugString __unused =
  927. [NSString stringWithFormat:@"APNS Environment in profile: %@", apsEnvironment];
  928. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID013, @"%@", debugString);
  929. // No aps-environment in the profile.
  930. if (!apsEnvironment.length) {
  931. [self logAPNSConfigurationError:@"No aps-environment set. If testing on a device APNS is not "
  932. @"correctly configured. Please recheck your provisioning "
  933. @"profiles. If testing on a simulator this is fine since APNS "
  934. @"doesn't work on the simulator."];
  935. return defaultAppTypeProd;
  936. }
  937. if ([apsEnvironment isEqualToString:kAPSEnvironmentDevelopmentValue]) {
  938. return NO;
  939. }
  940. return defaultAppTypeProd;
  941. }
  942. /// Log error messages only when Messaging exists in the pod.
  943. - (void)logAPNSConfigurationError:(NSString *)errorString {
  944. BOOL hasFirebaseMessaging = NSClassFromString(kFIRInstanceIDFCMSDKClassString) != nil;
  945. if (hasFirebaseMessaging) {
  946. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID014, @"%@", errorString);
  947. } else {
  948. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID015, @"%@", errorString);
  949. }
  950. }
  951. #pragma mark - Sync InstanceID
  952. - (void)updateFirebaseInstallationID {
  953. FIRInstanceID_WEAKIFY(self);
  954. [self.installations
  955. installationIDWithCompletion:^(NSString *_Nullable installationID, NSError *_Nullable error) {
  956. FIRInstanceID_STRONGIFY(self);
  957. self.firebaseInstallationsID = installationID;
  958. }];
  959. }
  960. - (void)installationIDDidChangeNotificationReceived:(NSNotification *)notification {
  961. NSString *installationAppID =
  962. notification.userInfo[kFIRInstallationIDDidChangeNotificationAppNameKey];
  963. if ([installationAppID isKindOfClass:[NSString class]] &&
  964. [installationAppID isEqual:self.firebaseAppID]) {
  965. [self updateFirebaseInstallationID];
  966. }
  967. }
  968. - (void)observeFirebaseInstallationIDChanges {
  969. [[NSNotificationCenter defaultCenter] removeObserver:self
  970. name:FIRInstallationIDDidChangeNotification
  971. object:nil];
  972. [[NSNotificationCenter defaultCenter]
  973. addObserver:self
  974. selector:@selector(installationIDDidChangeNotificationReceived:)
  975. name:FIRInstallationIDDidChangeNotification
  976. object:nil];
  977. }
  978. @end