FIRInstanceID.m 45 KB

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