FIRRemoteConfig.m 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  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 "FirebaseRemoteConfig/Sources/Public/FirebaseRemoteConfig/FIRRemoteConfig.h"
  17. #import "FirebaseABTesting/Sources/Private/FirebaseABTestingInternal.h"
  18. #import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
  19. #import "FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.h"
  20. #import "FirebaseRemoteConfig/Sources/Private/FIRRemoteConfig_Private.h"
  21. #import "FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h"
  22. #import "FirebaseRemoteConfig/Sources/Private/RCNConfigSettings.h"
  23. #import "FirebaseRemoteConfig/Sources/RCNConfigConstants.h"
  24. #import "FirebaseRemoteConfig/Sources/RCNConfigContent.h"
  25. #import "FirebaseRemoteConfig/Sources/RCNConfigDBManager.h"
  26. #import "FirebaseRemoteConfig/Sources/RCNConfigExperiment.h"
  27. #import "FirebaseRemoteConfig/Sources/RCNConfigValue_Internal.h"
  28. #import "FirebaseRemoteConfig/Sources/RCNDevice.h"
  29. /// Remote Config Error Domain.
  30. /// TODO: Rename according to obj-c style for constants.
  31. NSString *const FIRRemoteConfigErrorDomain = @"com.google.remoteconfig.ErrorDomain";
  32. /// Remote Config Error Info End Time Seconds;
  33. NSString *const FIRRemoteConfigThrottledEndTimeInSecondsKey = @"error_throttled_end_time_seconds";
  34. /// Minimum required time interval between fetch requests made to the backend.
  35. static NSString *const kRemoteConfigMinimumFetchIntervalKey = @"_rcn_minimum_fetch_interval";
  36. /// Timeout value for waiting on a fetch response.
  37. static NSString *const kRemoteConfigFetchTimeoutKey = @"_rcn_fetch_timeout";
  38. @implementation FIRRemoteConfigSettings
  39. - (instancetype)init {
  40. self = [super init];
  41. if (self) {
  42. _minimumFetchInterval = RCNDefaultMinimumFetchInterval;
  43. _fetchTimeout = RCNHTTPDefaultConnectionTimeout;
  44. }
  45. return self;
  46. }
  47. @end
  48. @implementation FIRRemoteConfig {
  49. /// All the config content.
  50. RCNConfigContent *_configContent;
  51. RCNConfigDBManager *_DBManager;
  52. RCNConfigSettings *_settings;
  53. RCNConfigFetch *_configFetch;
  54. RCNConfigExperiment *_configExperiment;
  55. dispatch_queue_t _queue;
  56. NSString *_appName;
  57. }
  58. static NSMutableDictionary<NSString *, NSMutableDictionary<NSString *, FIRRemoteConfig *> *>
  59. *RCInstances;
  60. + (nonnull FIRRemoteConfig *)remoteConfigWithApp:(FIRApp *_Nonnull)firebaseApp {
  61. return [FIRRemoteConfig remoteConfigWithFIRNamespace:FIRNamespaceGoogleMobilePlatform
  62. app:firebaseApp];
  63. }
  64. + (nonnull FIRRemoteConfig *)remoteConfigWithFIRNamespace:(NSString *_Nonnull)firebaseNamespace {
  65. if (![FIRApp isDefaultAppConfigured]) {
  66. FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000047",
  67. @"FIRApp not configured. Please make sure you have called [FIRApp configure]");
  68. // TODO: Maybe throw an exception here? That'd be a breaking change though, but at this point
  69. // RC can't work as expected.
  70. }
  71. return [FIRRemoteConfig remoteConfigWithFIRNamespace:firebaseNamespace app:[FIRApp defaultApp]];
  72. }
  73. + (nonnull FIRRemoteConfig *)remoteConfigWithFIRNamespace:(NSString *_Nonnull)firebaseNamespace
  74. app:(FIRApp *_Nonnull)firebaseApp {
  75. // Use the provider to generate and return instances of FIRRemoteConfig for this specific app and
  76. // namespace. This will ensure the app is configured before Remote Config can return an instance.
  77. id<FIRRemoteConfigProvider> provider =
  78. FIR_COMPONENT(FIRRemoteConfigProvider, firebaseApp.container);
  79. return [provider remoteConfigForNamespace:firebaseNamespace];
  80. }
  81. + (FIRRemoteConfig *)remoteConfig {
  82. // If the default app is not configured at this point, warn the developer.
  83. if (![FIRApp isDefaultAppConfigured]) {
  84. FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000047",
  85. @"FIRApp not configured. Please make sure you have called [FIRApp configure]");
  86. // TODO: Maybe throw an exception here? That'd be a breaking change though, but at this point
  87. // RC can't work as expected.
  88. }
  89. return [FIRRemoteConfig remoteConfigWithFIRNamespace:FIRNamespaceGoogleMobilePlatform
  90. app:[FIRApp defaultApp]];
  91. }
  92. /// Singleton instance of serial queue for queuing all incoming RC calls.
  93. + (dispatch_queue_t)sharedRemoteConfigSerialQueue {
  94. static dispatch_once_t onceToken;
  95. static dispatch_queue_t sharedRemoteConfigQueue;
  96. dispatch_once(&onceToken, ^{
  97. sharedRemoteConfigQueue =
  98. dispatch_queue_create(RCNRemoteConfigQueueLabel, DISPATCH_QUEUE_SERIAL);
  99. });
  100. return sharedRemoteConfigQueue;
  101. }
  102. /// Designated initializer
  103. - (instancetype)initWithAppName:(NSString *)appName
  104. FIROptions:(FIROptions *)options
  105. namespace:(NSString *)FIRNamespace
  106. DBManager:(RCNConfigDBManager *)DBManager
  107. configContent:(RCNConfigContent *)configContent
  108. analytics:(nullable id<FIRAnalyticsInterop>)analytics {
  109. self = [super init];
  110. if (self) {
  111. _appName = appName;
  112. _DBManager = DBManager;
  113. // The fully qualified Firebase namespace is namespace:firappname.
  114. _FIRNamespace = [NSString stringWithFormat:@"%@:%@", FIRNamespace, appName];
  115. // Initialize RCConfigContent if not already.
  116. _configContent = configContent;
  117. _settings = [[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager
  118. namespace:_FIRNamespace
  119. firebaseAppName:appName
  120. googleAppID:options.googleAppID];
  121. FIRExperimentController *experimentController = [FIRExperimentController sharedInstance];
  122. _configExperiment = [[RCNConfigExperiment alloc] initWithDBManager:_DBManager
  123. experimentController:experimentController];
  124. /// Serial queue for read and write lock.
  125. _queue = [FIRRemoteConfig sharedRemoteConfigSerialQueue];
  126. // Initialize with default config settings.
  127. [self setDefaultConfigSettings];
  128. _configFetch = [[RCNConfigFetch alloc] initWithContent:_configContent
  129. DBManager:_DBManager
  130. settings:_settings
  131. analytics:analytics
  132. experiment:_configExperiment
  133. queue:_queue
  134. namespace:_FIRNamespace
  135. options:options];
  136. [_settings loadConfigFromMetadataTable];
  137. }
  138. return self;
  139. }
  140. // Initialize with default config settings.
  141. - (void)setDefaultConfigSettings {
  142. // Set the default config settings.
  143. self->_settings.fetchTimeout = RCNHTTPDefaultConnectionTimeout;
  144. self->_settings.minimumFetchInterval = RCNDefaultMinimumFetchInterval;
  145. }
  146. - (void)ensureInitializedWithCompletionHandler:
  147. (nonnull FIRRemoteConfigInitializationCompletion)completionHandler {
  148. __weak FIRRemoteConfig *weakSelf = self;
  149. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
  150. FIRRemoteConfig *strongSelf = weakSelf;
  151. if (!strongSelf) {
  152. return;
  153. }
  154. BOOL initializationSuccess = [self->_configContent initializationSuccessful];
  155. NSError *error = nil;
  156. if (!initializationSuccess) {
  157. error = [[NSError alloc]
  158. initWithDomain:FIRRemoteConfigErrorDomain
  159. code:FIRRemoteConfigErrorInternalError
  160. userInfo:@{NSLocalizedDescriptionKey : @"Timed out waiting for database load."}];
  161. }
  162. completionHandler(error);
  163. });
  164. }
  165. #pragma mark - fetch
  166. - (void)fetchWithCompletionHandler:(FIRRemoteConfigFetchCompletion)completionHandler {
  167. dispatch_async(_queue, ^{
  168. [self fetchWithExpirationDuration:self->_settings.minimumFetchInterval
  169. completionHandler:completionHandler];
  170. });
  171. }
  172. - (void)fetchWithExpirationDuration:(NSTimeInterval)expirationDuration
  173. completionHandler:(FIRRemoteConfigFetchCompletion)completionHandler {
  174. FIRRemoteConfigFetchCompletion completionHandlerCopy = nil;
  175. if (completionHandler) {
  176. completionHandlerCopy = [completionHandler copy];
  177. }
  178. [_configFetch fetchConfigWithExpirationDuration:expirationDuration
  179. completionHandler:completionHandlerCopy];
  180. }
  181. #pragma mark - fetchAndActivate
  182. - (void)fetchAndActivateWithCompletionHandler:
  183. (FIRRemoteConfigFetchAndActivateCompletion)completionHandler {
  184. __weak FIRRemoteConfig *weakSelf = self;
  185. FIRRemoteConfigFetchCompletion fetchCompletion =
  186. ^(FIRRemoteConfigFetchStatus fetchStatus, NSError *fetchError) {
  187. FIRRemoteConfig *strongSelf = weakSelf;
  188. if (!strongSelf) {
  189. return;
  190. }
  191. // Fetch completed. We are being called on the main queue.
  192. // If fetch is successful, try to activate the fetched config
  193. if (fetchStatus == FIRRemoteConfigFetchStatusSuccess && !fetchError) {
  194. [strongSelf activateWithCompletion:^(BOOL changed, NSError *_Nullable activateError) {
  195. if (completionHandler) {
  196. FIRRemoteConfigFetchAndActivateStatus status =
  197. activateError ? FIRRemoteConfigFetchAndActivateStatusSuccessUsingPreFetchedData
  198. : FIRRemoteConfigFetchAndActivateStatusSuccessFetchedFromRemote;
  199. dispatch_async(dispatch_get_main_queue(), ^{
  200. completionHandler(status, nil);
  201. });
  202. }
  203. }];
  204. } else if (completionHandler) {
  205. FIRRemoteConfigFetchAndActivateStatus status =
  206. fetchStatus == FIRRemoteConfigFetchStatusSuccess
  207. ? FIRRemoteConfigFetchAndActivateStatusSuccessUsingPreFetchedData
  208. : FIRRemoteConfigFetchAndActivateStatusError;
  209. dispatch_async(dispatch_get_main_queue(), ^{
  210. completionHandler(status, fetchError);
  211. });
  212. }
  213. };
  214. [self fetchWithCompletionHandler:fetchCompletion];
  215. }
  216. #pragma mark - apply
  217. typedef void (^FIRRemoteConfigActivateChangeCompletion)(BOOL changed, NSError *_Nullable error);
  218. - (void)activateWithCompletion:(FIRRemoteConfigActivateChangeCompletion)completion {
  219. __weak FIRRemoteConfig *weakSelf = self;
  220. void (^applyBlock)(void) = ^(void) {
  221. FIRRemoteConfig *strongSelf = weakSelf;
  222. if (!strongSelf) {
  223. NSError *error = [NSError errorWithDomain:FIRRemoteConfigErrorDomain
  224. code:FIRRemoteConfigErrorInternalError
  225. userInfo:@{@"ActivationFailureReason" : @"Internal Error."}];
  226. if (completion) {
  227. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  228. completion(NO, error);
  229. });
  230. }
  231. FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000068", @"Internal error activating config.");
  232. return;
  233. }
  234. // Check if the last fetched config has already been activated. Fetches with no data change are
  235. // ignored.
  236. if (strongSelf->_settings.lastETagUpdateTime == 0 ||
  237. strongSelf->_settings.lastETagUpdateTime <= strongSelf->_settings.lastApplyTimeInterval) {
  238. FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000069",
  239. @"Most recently fetched config is already activated.");
  240. if (completion) {
  241. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  242. completion(NO, nil);
  243. });
  244. }
  245. return;
  246. }
  247. [strongSelf->_configContent copyFromDictionary:self->_configContent.fetchedConfig
  248. toSource:RCNDBSourceActive
  249. forNamespace:self->_FIRNamespace];
  250. strongSelf->_settings.lastApplyTimeInterval = [[NSDate date] timeIntervalSince1970];
  251. FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000069", @"Config activated.");
  252. [strongSelf->_configExperiment updateExperimentsWithHandler:^(NSError *_Nullable error) {
  253. if (completion) {
  254. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  255. completion(YES, nil);
  256. });
  257. }
  258. }];
  259. };
  260. dispatch_async(_queue, applyBlock);
  261. }
  262. #pragma mark - helpers
  263. - (NSString *)fullyQualifiedNamespace:(NSString *)namespace {
  264. // If this is already a fully qualified namespace, return.
  265. if ([namespace rangeOfString:@":"].location != NSNotFound) {
  266. return namespace;
  267. }
  268. NSString *fullyQualifiedNamespace = [NSString stringWithFormat:@"%@:%@", namespace, _appName];
  269. return fullyQualifiedNamespace;
  270. }
  271. #pragma mark - Get Config Result
  272. - (FIRRemoteConfigValue *)objectForKeyedSubscript:(NSString *)key {
  273. return [self configValueForKey:key];
  274. }
  275. - (FIRRemoteConfigValue *)configValueForKey:(NSString *)key {
  276. if (!key) {
  277. return [[FIRRemoteConfigValue alloc] initWithData:[NSData data]
  278. source:FIRRemoteConfigSourceStatic];
  279. }
  280. NSString *FQNamespace = [self fullyQualifiedNamespace:_FIRNamespace];
  281. __block FIRRemoteConfigValue *value;
  282. dispatch_sync(_queue, ^{
  283. value = self->_configContent.activeConfig[FQNamespace][key];
  284. if (value) {
  285. if (value.source != FIRRemoteConfigSourceRemote) {
  286. FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000001",
  287. @"Key %@ should come from source:%zd instead coming from source: %zd.", key,
  288. (long)FIRRemoteConfigSourceRemote, (long)value.source);
  289. }
  290. return;
  291. }
  292. value = self->_configContent.defaultConfig[FQNamespace][key];
  293. if (value) {
  294. return;
  295. }
  296. value = [[FIRRemoteConfigValue alloc] initWithData:[NSData data]
  297. source:FIRRemoteConfigSourceStatic];
  298. });
  299. return value;
  300. }
  301. - (FIRRemoteConfigValue *)configValueForKey:(NSString *)key source:(FIRRemoteConfigSource)source {
  302. if (!key) {
  303. return [[FIRRemoteConfigValue alloc] initWithData:[NSData data]
  304. source:FIRRemoteConfigSourceStatic];
  305. }
  306. NSString *FQNamespace = [self fullyQualifiedNamespace:_FIRNamespace];
  307. __block FIRRemoteConfigValue *value;
  308. dispatch_sync(_queue, ^{
  309. if (source == FIRRemoteConfigSourceRemote) {
  310. value = self->_configContent.activeConfig[FQNamespace][key];
  311. } else if (source == FIRRemoteConfigSourceDefault) {
  312. value = self->_configContent.defaultConfig[FQNamespace][key];
  313. } else {
  314. value = [[FIRRemoteConfigValue alloc] initWithData:[NSData data]
  315. source:FIRRemoteConfigSourceStatic];
  316. }
  317. });
  318. return value;
  319. }
  320. - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
  321. objects:(id __unsafe_unretained[])stackbuf
  322. count:(NSUInteger)len {
  323. __block NSUInteger localValue;
  324. dispatch_sync(_queue, ^{
  325. localValue =
  326. [self->_configContent.activeConfig[self->_FIRNamespace] countByEnumeratingWithState:state
  327. objects:stackbuf
  328. count:len];
  329. });
  330. return localValue;
  331. }
  332. #pragma mark - Properties
  333. /// Last fetch completion time.
  334. - (NSDate *)lastFetchTime {
  335. __block NSDate *fetchTime;
  336. dispatch_sync(_queue, ^{
  337. NSTimeInterval lastFetchTime = self->_settings.lastFetchTimeInterval;
  338. fetchTime = [NSDate dateWithTimeIntervalSince1970:lastFetchTime];
  339. });
  340. return fetchTime;
  341. }
  342. - (FIRRemoteConfigFetchStatus)lastFetchStatus {
  343. __block FIRRemoteConfigFetchStatus currentStatus;
  344. dispatch_sync(_queue, ^{
  345. currentStatus = self->_settings.lastFetchStatus;
  346. });
  347. return currentStatus;
  348. }
  349. - (NSArray *)allKeysFromSource:(FIRRemoteConfigSource)source {
  350. __block NSArray *keys = [[NSArray alloc] init];
  351. dispatch_sync(_queue, ^{
  352. NSString *FQNamespace = [self fullyQualifiedNamespace:self->_FIRNamespace];
  353. switch (source) {
  354. case FIRRemoteConfigSourceDefault:
  355. if (self->_configContent.defaultConfig[FQNamespace]) {
  356. keys = [[self->_configContent.defaultConfig[FQNamespace] allKeys] copy];
  357. }
  358. break;
  359. case FIRRemoteConfigSourceRemote:
  360. if (self->_configContent.activeConfig[FQNamespace]) {
  361. keys = [[self->_configContent.activeConfig[FQNamespace] allKeys] copy];
  362. }
  363. break;
  364. default:
  365. break;
  366. }
  367. });
  368. return keys;
  369. }
  370. - (nonnull NSSet *)keysWithPrefix:(nullable NSString *)prefix {
  371. __block NSMutableSet *keys = [[NSMutableSet alloc] init];
  372. dispatch_sync(_queue, ^{
  373. NSString *FQNamespace = [self fullyQualifiedNamespace:self->_FIRNamespace];
  374. if (self->_configContent.activeConfig[FQNamespace]) {
  375. NSArray *allKeys = [self->_configContent.activeConfig[FQNamespace] allKeys];
  376. if (!prefix.length) {
  377. keys = [NSMutableSet setWithArray:allKeys];
  378. } else {
  379. for (NSString *key in allKeys) {
  380. if ([key hasPrefix:prefix]) {
  381. [keys addObject:key];
  382. }
  383. }
  384. }
  385. }
  386. });
  387. return [keys copy];
  388. }
  389. #pragma mark - Defaults
  390. - (void)setDefaults:(NSDictionary<NSString *, NSObject *> *)defaultConfig {
  391. NSString *FQNamespace = [self fullyQualifiedNamespace:_FIRNamespace];
  392. NSDictionary *defaultConfigCopy = [[NSDictionary alloc] init];
  393. if (defaultConfig) {
  394. defaultConfigCopy = [defaultConfig copy];
  395. }
  396. void (^setDefaultsBlock)(void) = ^(void) {
  397. NSDictionary *namespaceToDefaults = @{FQNamespace : defaultConfigCopy};
  398. [self->_configContent copyFromDictionary:namespaceToDefaults
  399. toSource:RCNDBSourceDefault
  400. forNamespace:FQNamespace];
  401. self->_settings.lastSetDefaultsTimeInterval = [[NSDate date] timeIntervalSince1970];
  402. };
  403. dispatch_async(_queue, setDefaultsBlock);
  404. }
  405. - (FIRRemoteConfigValue *)defaultValueForKey:(NSString *)key {
  406. NSString *FQNamespace = [self fullyQualifiedNamespace:_FIRNamespace];
  407. __block FIRRemoteConfigValue *value;
  408. dispatch_sync(_queue, ^{
  409. NSDictionary *defaultConfig = self->_configContent.defaultConfig;
  410. value = defaultConfig[FQNamespace][key];
  411. if (value) {
  412. if (value.source != FIRRemoteConfigSourceDefault) {
  413. FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000002",
  414. @"Key %@ should come from source:%zd instead coming from source: %zd", key,
  415. (long)FIRRemoteConfigSourceDefault, (long)value.source);
  416. }
  417. }
  418. });
  419. return value;
  420. }
  421. - (void)setDefaultsFromPlistFileName:(nullable NSString *)fileName {
  422. if (!fileName || fileName.length == 0) {
  423. FIRLogWarning(kFIRLoggerRemoteConfig, @"I-RCN000037",
  424. @"The plist file '%@' could not be found by Remote Config.", fileName);
  425. return;
  426. }
  427. NSArray *bundles = @[ [NSBundle mainBundle], [NSBundle bundleForClass:[self class]] ];
  428. for (NSBundle *bundle in bundles) {
  429. NSString *plistFile = [bundle pathForResource:fileName ofType:@"plist"];
  430. // Use the first one we find.
  431. if (plistFile) {
  432. NSDictionary *defaultConfig = [[NSDictionary alloc] initWithContentsOfFile:plistFile];
  433. if (defaultConfig) {
  434. [self setDefaults:defaultConfig];
  435. }
  436. return;
  437. }
  438. }
  439. FIRLogWarning(kFIRLoggerRemoteConfig, @"I-RCN000037",
  440. @"The plist file '%@' could not be found by Remote Config.", fileName);
  441. }
  442. #pragma mark - custom variables
  443. - (FIRRemoteConfigSettings *)configSettings {
  444. __block NSTimeInterval minimumFetchInterval = RCNDefaultMinimumFetchInterval;
  445. __block NSTimeInterval fetchTimeout = RCNHTTPDefaultConnectionTimeout;
  446. dispatch_sync(_queue, ^{
  447. minimumFetchInterval = self->_settings.minimumFetchInterval;
  448. fetchTimeout = self->_settings.fetchTimeout;
  449. });
  450. FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000066",
  451. @"Successfully read configSettings. Minimum Fetch Interval:%f, "
  452. @"Fetch timeout: %f",
  453. minimumFetchInterval, fetchTimeout);
  454. FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
  455. settings.minimumFetchInterval = minimumFetchInterval;
  456. settings.fetchTimeout = fetchTimeout;
  457. /// The NSURLSession needs to be recreated whenever the fetch timeout may be updated.
  458. [_configFetch recreateNetworkSession];
  459. return settings;
  460. }
  461. - (void)setConfigSettings:(FIRRemoteConfigSettings *)configSettings {
  462. void (^setConfigSettingsBlock)(void) = ^(void) {
  463. if (!configSettings) {
  464. return;
  465. }
  466. self->_settings.minimumFetchInterval = configSettings.minimumFetchInterval;
  467. self->_settings.fetchTimeout = configSettings.fetchTimeout;
  468. /// The NSURLSession needs to be recreated whenever the fetch timeout may be updated.
  469. [self->_configFetch recreateNetworkSession];
  470. FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000067",
  471. @"Successfully set configSettings. Minimum Fetch Interval:%f, "
  472. @"Fetch timeout:%f",
  473. configSettings.minimumFetchInterval, configSettings.fetchTimeout);
  474. };
  475. dispatch_async(_queue, setConfigSettingsBlock);
  476. }
  477. @end