@@ -21,19 +21,59 @@ import Foundation
2121 case fetched
2222}
2323
24+ /// The AtomicConfig class for the config variables enables atomic accesses to support multiple
25+ /// namespace usage of RemoteConfig.
26+ private class AtomicConfig {
27+ private var value : [ String : [ String : RemoteConfigValue ] ]
28+ private let lock = NSLock ( )
29+
30+ init ( _ value: [ String : [ String : RemoteConfigValue ] ] ) {
31+ self . value = value
32+ }
33+
34+ var wrappedValue : [ String : [ String : RemoteConfigValue ] ] {
35+ get { return load ( ) }
36+ set { store ( newValue: newValue) }
37+ }
38+
39+ func load( ) -> [ String : [ String : RemoteConfigValue ] ] {
40+ lock. lock ( )
41+ defer { lock. unlock ( ) }
42+ return value
43+ }
44+
45+ func store( newValue: [ String : [ String : RemoteConfigValue ] ] ) {
46+ lock. lock ( )
47+ defer { lock. unlock ( ) }
48+ value = newValue
49+ }
50+
51+ func update( namespace: String , newValue: [ String : RemoteConfigValue ] ) {
52+ lock. lock ( )
53+ defer { lock. unlock ( ) }
54+ value [ namespace] = newValue
55+ }
56+
57+ func update( namespace: String , key: String , rcValue: RemoteConfigValue ) {
58+ lock. lock ( )
59+ defer { lock. unlock ( ) }
60+ value [ namespace] ? [ key] = rcValue
61+ }
62+ }
63+
2464/// This class handles all the config content that is fetched from the server, cached in local
2565/// config or persisted in database.
2666@objc ( RCNConfigContent) public
2767class ConfigContent : NSObject {
2868 /// Active config data that is currently used.
29- private var _activeConfig : [ String : [ String : RemoteConfigValue ] ] = [ : ]
69+ private var _activeConfig = AtomicConfig ( [ : ] )
3070
3171 /// Pending config (aka Fetched config) data that is latest data from server that might or might
3272 /// not be applied.
33- private var _fetchedConfig : [ String : [ String : RemoteConfigValue ] ] = [ : ]
73+ private var _fetchedConfig = AtomicConfig ( [ : ] )
3474
3575 /// Default config provided by user.
36- private var _defaultConfig : [ String : [ String : RemoteConfigValue ] ] = [ : ]
76+ private var _defaultConfig = AtomicConfig ( [ : ] )
3777
3878 /// Active Personalization metadata that is currently used.
3979 private var _activePersonalization : [ String : Any ] = [ : ]
@@ -126,9 +166,9 @@ class ConfigContent: NSObject {
126166 dbManager. loadMain ( withBundleIdentifier: bundleIdentifier) { [ weak self] success,
127167 fetched, active, defaults, rolloutMetadata in
128168 guard let self = self else { return }
129- self . _fetchedConfig = fetched
130- self . _activeConfig = active
131- self . _defaultConfig = defaults
169+ self . _fetchedConfig. store ( newValue : fetched)
170+ self . _activeConfig. store ( newValue : active)
171+ self . _defaultConfig. store ( newValue : defaults)
132172 self
133173 . _fetchedRolloutMetadata =
134174 rolloutMetadata [ ConfigConstants . rolloutTableKeyFetchedMetadata] as? [ [ String : Any ] ] ?? [ ]
@@ -173,14 +213,14 @@ class ConfigContent: NSObject {
173213
174214 switch dbSource {
175215 case . default:
176- toDictionary = _defaultConfig
216+ toDictionary = defaultConfig ( )
177217 source = . default
178218 case . fetched:
179219 RCLog . warning ( " I-RCN000008 " ,
180220 " This shouldn't happen. Destination dictionary should never be pending type. " )
181221 return
182222 case . active:
183- toDictionary = _activeConfig
223+ toDictionary = activeConfig ( )
184224 source = . remote
185225 toDictionary. removeValue ( forKey: firebaseNamespace)
186226 }
@@ -241,9 +281,9 @@ class ConfigContent: NSObject {
241281 }
242282
243283 if dbSource == . default {
244- _defaultConfig = toDictionary
284+ _defaultConfig. store ( newValue : toDictionary)
245285 } else {
246- _activeConfig = toDictionary
286+ _activeConfig. store ( newValue : toDictionary)
247287 }
248288 }
249289
@@ -307,27 +347,23 @@ class ConfigContent: NSObject {
307347 // MARK: - State Handling
308348
309349 func handleNoChangeState( forConfigNamespace firebaseNamespace: String ) {
310- if _fetchedConfig [ firebaseNamespace] == nil {
311- _fetchedConfig [ firebaseNamespace] = [ : ]
350+ if fetchedConfig ( ) [ firebaseNamespace] == nil {
351+ _fetchedConfig. update ( namespace : firebaseNamespace, newValue : [ : ] )
312352 }
313353 }
314354
315355 func handleEmptyConfigState( forConfigNamespace firebaseNamespace: String ) {
316- if let _ = _fetchedConfig [ firebaseNamespace] {
317- _fetchedConfig [ firebaseNamespace] ? . removeAll ( )
318- } else {
319- // If namespace has empty status and it doesn't exist in _fetchedConfig, we will
320- // still add an entry for that namespace. Even if it will not be persisted in database.
321- _fetchedConfig [ firebaseNamespace] = [ : ]
322- }
356+ // If namespace has empty status and it doesn't exist in _fetchedConfig, we will
357+ // still add an entry for that namespace. Even if it will not be persisted in database.
358+ _fetchedConfig. update ( namespace: firebaseNamespace, newValue: [ : ] )
323359 dbManager? . deleteRecord ( fromMainTableWithNamespace: firebaseNamespace,
324360 bundleIdentifier: bundleIdentifier,
325361 fromSource: . fetched)
326362 }
327363
328364 func handleNoTemplateState( forConfigNamespace firebaseNamespace: String ) {
329365 // Remove the namespace.
330- _fetchedConfig. removeValue ( forKey : firebaseNamespace)
366+ _fetchedConfig. update ( namespace : firebaseNamespace, newValue : [ : ] )
331367 dbManager? . deleteRecord ( fromMainTableWithNamespace: firebaseNamespace,
332368 bundleIdentifier: bundleIdentifier,
333369 fromSource: . fetched)
@@ -341,17 +377,14 @@ class ConfigContent: NSObject {
341377 dbManager? . deleteRecord ( fromMainTableWithNamespace: firebaseNamespace,
342378 bundleIdentifier: bundleIdentifier,
343379 fromSource: . fetched)
344- if _fetchedConfig [ firebaseNamespace] != nil {
345- _fetchedConfig [ firebaseNamespace] ? . removeAll ( )
346- } else {
347- _fetchedConfig [ firebaseNamespace] = [ : ]
348- }
380+ _fetchedConfig. update ( namespace: firebaseNamespace, newValue: [ : ] )
349381
350382 // Store the fetched config values.
351383 for (key, value) in entries {
352384 guard let valueData = value. data ( using: . utf8) else { continue }
353- _fetchedConfig [ firebaseNamespace] ? [ key] = RemoteConfigValue ( data: valueData,
354- source: . remote)
385+ _fetchedConfig
386+ . update ( namespace: firebaseNamespace, key: key,
387+ rcValue: RemoteConfigValue ( data: valueData, source: . remote) )
355388 let values : [ Any ] = [ bundleIdentifier, firebaseNamespace, key, valueData]
356389 updateMainTable ( withValues: values, fromSource: . fetched)
357390 }
@@ -377,23 +410,23 @@ class ConfigContent: NSObject {
377410 /// If this is the first time reading the fetchedConfig, we might still be reading it from the
378411 /// database.
379412 checkAndWaitForInitialDatabaseLoad ( )
380- return _fetchedConfig
413+ return _fetchedConfig. wrappedValue
381414 }
382415
383416 @objc public
384- func activeConfig( ) -> [ String : Any ] {
417+ func activeConfig( ) -> [ String : [ String : RemoteConfigValue ] ] {
385418 /// If this is the first time reading the activeConfig, we might still be reading it from the
386419 /// database.
387420 checkAndWaitForInitialDatabaseLoad ( )
388- return _activeConfig
421+ return _activeConfig. wrappedValue
389422 }
390423
391424 @objc public
392- func defaultConfig( ) -> [ String : Any ] {
425+ func defaultConfig( ) -> [ String : [ String : RemoteConfigValue ] ] {
393426 /// If this is the first time reading the defaultConfig, we might still be reading it from the
394427 /// database.
395428 checkAndWaitForInitialDatabaseLoad ( )
396- return _defaultConfig
429+ return _defaultConfig. wrappedValue
397430 }
398431
399432 @objc public
@@ -420,7 +453,7 @@ class ConfigContent: NSObject {
420453 // database.
421454 checkAndWaitForInitialDatabaseLoad ( )
422455 return [
423- ConfigConstants . fetchResponseKeyEntries: _activeConfig [ firebaseNamespace] as Any ,
456+ ConfigConstants . fetchResponseKeyEntries: activeConfig ( ) [ firebaseNamespace] as Any ,
424457 ConfigConstants . fetchResponseKeyPersonalizationMetadata: activePersonalization,
425458 ]
426459 }
@@ -431,8 +464,8 @@ class ConfigContent: NSObject {
431464 // TODO: handle diff in experiment metadata.
432465 var updatedKeys = Set < String > ( )
433466
434- let fetchedConfig = _fetchedConfig [ firebaseNamespace] ?? [ : ]
435- let activeConfig = _activeConfig [ firebaseNamespace] ?? [ : ]
467+ let fetchedConfig = fetchedConfig ( ) [ firebaseNamespace] ?? [ : ]
468+ let activeConfig = activeConfig ( ) [ firebaseNamespace] ?? [ : ]
436469 let fetchedP13n = _fetchedPersonalization
437470 let activeP13n = _activePersonalization
438471 let fetchedRolloutMetadata = _fetchedRolloutMetadata
0 commit comments