From 15f973748c808033134f4a38b9253f6d78ecf6a9 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Wed, 22 Oct 2025 21:32:39 +0600 Subject: [PATCH 1/7] chore: update SDK links in README.md - Remove extra space at the end of the Ruby SDK link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d068f006..983acfbe 100644 --- a/README.md +++ b/README.md @@ -129,4 +129,4 @@ Used to enforce Swift style and conventions. - React - https://github.com/optimizely/react-sdk -- Ruby - https://github.com/optimizely/ruby-sdk \ No newline at end of file +- Ruby - https://github.com/optimizely/ruby-sdk \ No newline at end of file From 81a776c4a3e1d8e84311bb5cd6db1c5bd24d2318 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Thu, 30 Oct 2025 21:37:52 +0600 Subject: [PATCH 2/7] fix: update cmab retry configuration - Update max retries from 3 to 1 in CmabRetryConfig struct feat: add default values for cmab cache in DefaultCmabService - Add constants for DEFAULT_CMAB_CACHE_TIMEOUT and DEFAULT_CMAB_CACHE_SIZE - Assign DEFAULT_CMAB_CACHE_SIZE as 100 and DEFAULT_CMAB_CACHE_TIMEOUT as 600 - Change access control of cmabCache property to 'let' in DefaultCmabService feat: allow custom cache size and timeout for cmab cache in DefaultCmabService - Add new static function createDefault with parameters for cache size and timeout - Implement creation of DefaultCmabService with custom cache size and timeout feat: add cmab cache settings to OptimizelySdkSettings - Add cmabCacheSize and cmabCacheTitmeoutInSecs properties to OptimizelySdkSettings struct - Initialize properties with default values and update init method fix: update cmab cache settings initialization in OptimizelyClient - Update initialization of cmabService with size and timeout values from sdkSettings chore: refactor tests for cmab cache settings in OptimizelyClientTests_ODP - Modify test cases to check the cmabCacheSize and cmabCacheTitmeoutInSecs values build: remove unnecessary code from FakeDecisionService - Remove empty lines for cleanliness refactor: improve parameter passing in FakeDecisionService constructor - Modify the constructor to pass cmabService parameter explicitly in super.init --- Sources/CMAB/CmabClient.swift | 2 +- Sources/CMAB/CmabService.swift | 12 +++++++++--- Sources/ODP/OptimizelySdkSettings.swift | 11 +++++++++++ Sources/Optimizely/OptimizelyClient.swift | 2 +- .../OptimizelyClientTests_ODP.swift | 11 +++++++++-- .../DecisionListenerTests.swift | 3 +-- 6 files changed, 32 insertions(+), 9 deletions(-) diff --git a/Sources/CMAB/CmabClient.swift b/Sources/CMAB/CmabClient.swift index 1e69c3d9..e94f941c 100644 --- a/Sources/CMAB/CmabClient.swift +++ b/Sources/CMAB/CmabClient.swift @@ -32,7 +32,7 @@ enum CmabClientError: Error, Equatable { } struct CmabRetryConfig { - var maxRetries: Int = 3 + var maxRetries: Int = 1 var initialBackoff: TimeInterval = 0.1 // seconds var maxBackoff: TimeInterval = 10.0 // seconds var backoffMultiplier: Double = 2.0 diff --git a/Sources/CMAB/CmabService.swift b/Sources/CMAB/CmabService.swift index c74205e8..09eb71c3 100644 --- a/Sources/CMAB/CmabService.swift +++ b/Sources/CMAB/CmabService.swift @@ -41,11 +41,14 @@ protocol CmabService { completion: @escaping CmabDecisionCompletionHandler) } +let DEFAULT_CMAB_CACHE_TIMEOUT = 600 // secs +let DEFAULT_CMAB_CACHE_SIZE = 100 + class DefaultCmabService: CmabService { typealias UserAttributes = [String : Any?] private let cmabClient: CmabClient - private let cmabCache: LruCache + let cmabCache: LruCache private let logger = OPTLoggerFactory.getLogger() init(cmabClient: CmabClient, cmabCache: LruCache) { @@ -178,9 +181,12 @@ class DefaultCmabService: CmabService { extension DefaultCmabService { static func createDefault() -> DefaultCmabService { - let DEFAULT_CMAB_CACHE_TIMEOUT = 30 * 60 * 1000 // 30 minutes in milliseconds - let DEFAULT_CMAB_CACHE_SIZE = 1000 let cache = LruCache(size: DEFAULT_CMAB_CACHE_SIZE, timeoutInSecs: DEFAULT_CMAB_CACHE_TIMEOUT) return DefaultCmabService(cmabClient: DefaultCmabClient(), cmabCache: cache) } + + static func createDefault(cacheSize: Int, cacheTimeout: Int) -> DefaultCmabService { + let cache = LruCache(size: cacheSize, timeoutInSecs: cacheTimeout) + return DefaultCmabService(cmabClient: DefaultCmabClient(), cmabCache: cache) + } } diff --git a/Sources/ODP/OptimizelySdkSettings.swift b/Sources/ODP/OptimizelySdkSettings.swift index fc3d4713..3065380e 100644 --- a/Sources/ODP/OptimizelySdkSettings.swift +++ b/Sources/ODP/OptimizelySdkSettings.swift @@ -25,11 +25,16 @@ public struct OptimizelySdkSettings { let timeoutForSegmentFetchInSecs: Int /// The timeout in seconds of odp event dispatch - OS default timeout will be used if this is set to zero. let timeoutForOdpEventInSecs: Int + /// The maximum size of cmab cache + let cmabCacheSize: Int + /// The timeout in seconds of cmab cache + let cmabCacheTitmeoutInSecs: Int /// ODP features are disabled if this is set to true. let disableOdp: Bool /// VUID is enabled if this is set to true. let enableVuid: Bool + /// Optimizely SDK Settings /// /// - Parameters: @@ -37,6 +42,8 @@ public struct OptimizelySdkSettings { /// - segmentsCacheTimeoutInSecs: The timeout in seconds of audience segments cache (optional. default = 600). Set to zero to disable timeout. /// - timeoutForSegmentFetchInSecs: The timeout in seconds of odp segment fetch (optional. default = 10) - OS default timeout will be used if this is set to zero. /// - timeoutForOdpEventInSecs: The timeout in seconds of odp event dispatch (optional. default = 10) - OS default timeout will be used if this is set to zero. + /// - cmabCacheSize: The maximum size of cmab cache (optional. default = 100). Set to zero to disable caching. + /// - cmabCacheTitmeoutInSecs: The timeout in seconds of amb cache (optional. default = 600). Set to zero to disable timeout. /// - disableOdp: Set this flag to true (default = false) to disable ODP features /// - enableVuid: Set this flag to true (default = false) to enable vuid. /// - sdkName: Set this flag to override sdkName included in events @@ -45,12 +52,16 @@ public struct OptimizelySdkSettings { segmentsCacheTimeoutInSecs: Int = 600, timeoutForSegmentFetchInSecs: Int = 10, timeoutForOdpEventInSecs: Int = 10, + cmabCacheSize: Int = 100, + cmabCacheTitmeoutInSecs: Int = 600, disableOdp: Bool = false, enableVuid: Bool = false, sdkName: String? = nil, sdkVersion: String? = nil) { self.segmentsCacheSize = segmentsCacheSize self.segmentsCacheTimeoutInSecs = segmentsCacheTimeoutInSecs + self.cmabCacheSize = cmabCacheSize + self.cmabCacheTitmeoutInSecs = cmabCacheTitmeoutInSecs self.timeoutForSegmentFetchInSecs = timeoutForSegmentFetchInSecs self.timeoutForOdpEventInSecs = timeoutForOdpEventInSecs self.disableOdp = disableOdp diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index 8dcf525f..74d0af25 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -108,7 +108,7 @@ open class OptimizelyClient: NSObject { let logger = logger ?? DefaultLogger() type(of: logger).logLevel = defaultLogLevel ?? .info - let cmabService = DefaultCmabService.createDefault() + let cmabService = DefaultCmabService.createDefault(cacheSize: self.sdkSettings.cmabCacheSize, cacheTimeout: self.sdkSettings.cmabCacheTitmeoutInSecs) self.registerServices(sdkKey: sdkKey, logger: logger, diff --git a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift index 882aa717..dfe8f324 100644 --- a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift +++ b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift @@ -37,7 +37,9 @@ class OptimizelyClientTests_ODP: XCTestCase { func testSdkSettings_default() { let optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey) - + let cmabCache = ((optimizely.decisionService as! DefaultDecisionService).cmabService as! DefaultCmabService).cmabCache + XCTAssertEqual(100, cmabCache.maxSize) + XCTAssertEqual(600, cmabCache.timeoutInSecs) XCTAssertEqual(100, optimizely.odpManager.segmentManager?.segmentsCache.maxSize) XCTAssertEqual(600, optimizely.odpManager.segmentManager?.segmentsCache.timeoutInSecs) XCTAssertEqual(10, optimizely.odpManager.segmentManager?.apiMgr.resourceTimeoutInSecs) @@ -47,8 +49,13 @@ class OptimizelyClientTests_ODP: XCTestCase { func testSdkSettings_custom() { var sdkSettings = OptimizelySdkSettings(segmentsCacheSize: 12, - segmentsCacheTimeoutInSecs: 345) + segmentsCacheTimeoutInSecs: 345, + cmabCacheSize: 50, + cmabCacheTitmeoutInSecs: 120) var optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: sdkSettings) + let cmabCache = ((optimizely.decisionService as! DefaultDecisionService).cmabService as! DefaultCmabService).cmabCache + XCTAssertEqual(50, cmabCache.maxSize) + XCTAssertEqual(120, cmabCache.timeoutInSecs) XCTAssertEqual(12, optimizely.odpManager.segmentManager?.segmentsCache.maxSize) XCTAssertEqual(345, optimizely.odpManager.segmentManager?.segmentsCache.timeoutInSecs) diff --git a/Tests/OptimizelyTests-Common/DecisionListenerTests.swift b/Tests/OptimizelyTests-Common/DecisionListenerTests.swift index 8e885b2a..30c85d42 100644 --- a/Tests/OptimizelyTests-Common/DecisionListenerTests.swift +++ b/Tests/OptimizelyTests-Common/DecisionListenerTests.swift @@ -1246,13 +1246,12 @@ class FakeManager: OptimizelyClient { } class FakeDecisionService: DefaultDecisionService { - var experiment: Experiment? var variation: Variation? var source: String! override init(userProfileService: OPTUserProfileService, cmabService: CmabService = DefaultCmabService.createDefault()) { - super.init(userProfileService: DefaultUserProfileService()) + super.init(userProfileService: DefaultUserProfileService(), cmabService: cmabService) } override func getVariationForFeature(config: ProjectConfig, From e2055a03184db1951bc09788dddddc29cb273e79 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Thu, 30 Oct 2025 22:46:27 +0600 Subject: [PATCH 3/7] chore: add CmabCache.swift and update references in project files --- OptimizelySwiftSDK.xcodeproj/project.pbxproj | 34 +++ Sources/CMAB/CmabCache.swift | 26 +++ Sources/CMAB/CmabService.swift | 8 +- Sources/ODP/OptimizelySdkSettings.swift | 8 +- Sources/Optimizely/OptimizelyClient.swift | 2 +- .../OptimizelyClientTests_ODP.swift | 2 +- .../CmabServiceTests.swift | 221 +++++++++++++++++- .../DecisionServiceTests_Experiments.swift | 2 +- ...imizelyUserContextTests_Decide_Async.swift | 2 +- ...timizelyUserContextTests_Decide_CMAB.swift | 2 +- 10 files changed, 292 insertions(+), 15 deletions(-) create mode 100644 Sources/CMAB/CmabCache.swift diff --git a/OptimizelySwiftSDK.xcodeproj/project.pbxproj b/OptimizelySwiftSDK.xcodeproj/project.pbxproj index a442597c..0231ec92 100644 --- a/OptimizelySwiftSDK.xcodeproj/project.pbxproj +++ b/OptimizelySwiftSDK.xcodeproj/project.pbxproj @@ -2078,6 +2078,22 @@ 98AC984B2DB8FFE0001405DD /* DecisionServiceTests_Holdouts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98AC98482DB8FC29001405DD /* DecisionServiceTests_Holdouts.swift */; }; 98AC985E2DBA6721001405DD /* OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98AC985D2DBA6721001405DD /* OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift */; }; 98AC985F2DBA6721001405DD /* OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98AC985D2DBA6721001405DD /* OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift */; }; + 98CBECAC2EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; + 98CBECAD2EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; + 98CBECAE2EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; + 98CBECAF2EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; + 98CBECB02EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; + 98CBECB12EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; + 98CBECB22EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; + 98CBECB32EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; + 98CBECB42EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; + 98CBECB52EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; + 98CBECB62EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; + 98CBECB72EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; + 98CBECB82EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; + 98CBECB92EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; + 98CBECBA2EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; + 98CBECBB2EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; 98D5AE842DBB91C0000D5844 /* OptimizelyUserContextTests_Decide_Holdouts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D5AE832DBB91C0000D5844 /* OptimizelyUserContextTests_Decide_Holdouts.swift */; }; 98D5AE852DBB91C0000D5844 /* OptimizelyUserContextTests_Decide_Holdouts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D5AE832DBB91C0000D5844 /* OptimizelyUserContextTests_Decide_Holdouts.swift */; }; 98F28A1D2E01940500A86546 /* Cmab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98F28A1C2E01940500A86546 /* Cmab.swift */; }; @@ -2587,6 +2603,7 @@ 98AC98452DB7B762001405DD /* BucketTests_HoldoutToVariation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BucketTests_HoldoutToVariation.swift; sourceTree = ""; }; 98AC98482DB8FC29001405DD /* DecisionServiceTests_Holdouts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecisionServiceTests_Holdouts.swift; sourceTree = ""; }; 98AC985D2DBA6721001405DD /* OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift; sourceTree = ""; }; + 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CmabCache.swift; sourceTree = ""; }; 98D5AE832DBB91C0000D5844 /* OptimizelyUserContextTests_Decide_Holdouts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptimizelyUserContextTests_Decide_Holdouts.swift; sourceTree = ""; }; 98F28A1C2E01940500A86546 /* Cmab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cmab.swift; sourceTree = ""; }; 98F28A2D2E01968000A86546 /* CmabTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CmabTests.swift; sourceTree = ""; }; @@ -3304,6 +3321,7 @@ children = ( 98F28A402E02DD6D00A86546 /* CmabClient.swift */, 98F28A552E0451CC00A86546 /* CmabService.swift */, + 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */, ); path = CMAB; sourceTree = ""; @@ -4321,6 +4339,7 @@ 989428BB2DBFA431008BA1C8 /* MockBucketer.swift in Sources */, 6E14CDAB2423F9EB00010234 /* MockUrlSession.swift in Sources */, 6E14CDAA2423F9C300010234 /* SDKVersion.swift in Sources */, + 98CBECB32EB3CA6500C06A62 /* CmabCache.swift in Sources */, 845945C3287758A100D13E11 /* OdpConfig.swift in Sources */, 6E14CD832423F9A100010234 /* DataStoreQueueStackImpl.swift in Sources */, 848617F12863E21200B7F41B /* OdpEventApiManager.swift in Sources */, @@ -4475,6 +4494,7 @@ 6E424D0926324B620081004A /* FeatureVariable.swift in Sources */, 6E6419DA2657059700C49555 /* NotificationCenterTests_MultiClients.swift in Sources */, 6E424D0A26324B620081004A /* Rollout.swift in Sources */, + 98CBECB52EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E2D5DAE26338CA00002077F /* AtomicDictionaryTests.swift in Sources */, 6E424D0B26324B620081004A /* Variation.swift in Sources */, 6E424D0C26324B620081004A /* TrafficAllocation.swift in Sources */, @@ -4606,6 +4626,7 @@ 6EF8DE1B24BD1BB2008B9488 /* OptimizelyDecideOption.swift in Sources */, 980CC90D2D833F2800E07D24 /* ExperimentCore.swift in Sources */, 6E75173722C520D400B2B157 /* MurmurHash3.swift in Sources */, + 98CBECB42EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E7517F922C520D400B2B157 /* DataStoreUserDefaults.swift in Sources */, C78CAF592445AD8D009FE876 /* OptimizelyJSON.swift in Sources */, 848617C92863DC2700B7F41B /* OdpSegmentManager.swift in Sources */, @@ -4648,6 +4669,7 @@ 989428B32DBFA431008BA1C8 /* MockBucketer.swift in Sources */, 6E75170222C520D400B2B157 /* OptimizelyLogLevel.swift in Sources */, 6E7516BA22C520D400B2B157 /* DefaultUserProfileService.swift in Sources */, + 98CBECB02EB3CA6500C06A62 /* CmabCache.swift in Sources */, 845945C7287758A300D13E11 /* OdpConfig.swift in Sources */, 6E75175622C520D400B2B157 /* LogMessage.swift in Sources */, 848617F62863E21200B7F41B /* OdpEventApiManager.swift in Sources */, @@ -4866,6 +4888,7 @@ 6E75175E22C520D400B2B157 /* AtomicProperty.swift in Sources */, 6E9B11DE22C548A200C22D81 /* OptimizelyClientTests_Others.swift in Sources */, C78CAF7424482C86009FE876 /* OptimizelyClientTests_OptimizelyJSON.swift in Sources */, + 98CBECBA2EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6EC6DD3724ABF6990017D296 /* OptimizelyClient+Decide.swift in Sources */, 6E7516E622C520D400B2B157 /* OPTEventDispatcher.swift in Sources */, 98F28A612E0451CC00A86546 /* CmabService.swift in Sources */, @@ -4925,6 +4948,7 @@ 6E75173D22C520D400B2B157 /* MurmurHash3.swift in Sources */, 6E7516E922C520D400B2B157 /* OPTEventDispatcher.swift in Sources */, 6E7518A722C520D400B2B157 /* FeatureFlag.swift in Sources */, + 98CBECB92EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E75187722C520D400B2B157 /* Variation.swift in Sources */, 6E7517F322C520D400B2B157 /* DataStoreMemory.swift in Sources */, 6E7518FB22C520D500B2B157 /* UserAttribute.swift in Sources */, @@ -5095,6 +5119,7 @@ 84E2E94F2852A378001114AB /* VuidManager.swift in Sources */, 6E75176322C520D400B2B157 /* AtomicProperty.swift in Sources */, 6E9B117722C5487100C22D81 /* BatchEventBuilderTests_EventTags.swift in Sources */, + 98CBECB62EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E7517DD22C520D400B2B157 /* DefaultNotificationCenter.swift in Sources */, 6E9B116622C5487100C22D81 /* DecisionServiceTests_UserProfiles.swift in Sources */, 6E34A6202319EBB800BAE302 /* Notifications.swift in Sources */, @@ -5252,6 +5277,7 @@ 6E9B11B822C5489600C22D81 /* OTUtils.swift in Sources */, 98AC97F42DAE9685001405DD /* HoldoutConfigTests.swift in Sources */, 6E9B119022C5488300C22D81 /* AttributeValueTests.swift in Sources */, + 98CBECBB2EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E994B4025A3E6EA00999262 /* DecisionResponse.swift in Sources */, 84E2E9802855875E001114AB /* OdpEventManager.swift in Sources */, 6E75175822C520D400B2B157 /* LogMessage.swift in Sources */, @@ -5390,6 +5416,7 @@ 6E4544AF270E67C800F2CEBC /* NetworkReachability.swift in Sources */, 6E75177522C520D400B2B157 /* SDKVersion.swift in Sources */, 848617CD2863DC2700B7F41B /* OdpSegmentManager.swift in Sources */, + 98CBECAE2EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E75180722C520D400B2B157 /* DataStoreFile.swift in Sources */, 8486181D286D188B00B7F41B /* OdpEventApiManagerTests.swift in Sources */, 6E75183722C520D400B2B157 /* EventForDispatch.swift in Sources */, @@ -5572,6 +5599,7 @@ 6E424C04263228FD0081004A /* AtomicDictionary.swift in Sources */, 6E75189922C520D400B2B157 /* Experiment.swift in Sources */, 6E75178322C520D400B2B157 /* ArrayEventForDispatch+Extension.swift in Sources */, + 98CBECB12EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E424BE5263228E90081004A /* AtomicArray.swift in Sources */, 6E7518A522C520D400B2B157 /* FeatureFlag.swift in Sources */, ); @@ -5638,6 +5666,7 @@ 6E4544B4270E67C800F2CEBC /* NetworkReachability.swift in Sources */, 6E75184622C520D400B2B157 /* Event.swift in Sources */, 6E7517CE22C520D400B2B157 /* DefaultBucketer.swift in Sources */, + 98CBECB82EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E75180A22C520D400B2B157 /* DataStoreFile.swift in Sources */, 6E7516B822C520D400B2B157 /* DefaultUserProfileService.swift in Sources */, 6E7517C222C520D400B2B157 /* DefaultDatafileHandler.swift in Sources */, @@ -5747,6 +5776,7 @@ 6E4544B9270E67C800F2CEBC /* NetworkReachability.swift in Sources */, 6E75184B22C520D400B2B157 /* Event.swift in Sources */, 6E7517D322C520D400B2B157 /* DefaultBucketer.swift in Sources */, + 98CBECAD2EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E75180F22C520D400B2B157 /* DataStoreFile.swift in Sources */, 6E7516BD22C520D400B2B157 /* DefaultUserProfileService.swift in Sources */, 6E7517C722C520D400B2B157 /* DefaultDatafileHandler.swift in Sources */, @@ -5861,6 +5891,7 @@ 6EF8DE1A24BD1BB2008B9488 /* OptimizelyDecideOption.swift in Sources */, 980CC90B2D833F2800E07D24 /* ExperimentCore.swift in Sources */, 6E75186422C520D400B2B157 /* Rollout.swift in Sources */, + 98CBECAF2EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E75179622C520D400B2B157 /* DataStoreQueueStackImpl+Extension.swift in Sources */, C78CAF582445AD8D009FE876 /* OptimizelyJSON.swift in Sources */, 848617C82863DC2700B7F41B /* OdpSegmentManager.swift in Sources */, @@ -5903,6 +5934,7 @@ 989428BE2DBFA431008BA1C8 /* MockBucketer.swift in Sources */, 6E7516FC22C520D400B2B157 /* OptimizelyLogLevel.swift in Sources */, 6E7516B422C520D400B2B157 /* DefaultUserProfileService.swift in Sources */, + 98CBECAC2EB3CA6500C06A62 /* CmabCache.swift in Sources */, 845945C02877589F00D13E11 /* OdpConfig.swift in Sources */, 6E75175022C520D400B2B157 /* LogMessage.swift in Sources */, 848617EE2863E21200B7F41B /* OdpEventApiManager.swift in Sources */, @@ -6089,6 +6121,7 @@ 75C71A3B25E454460084187E /* ArrayEventForDispatch+Extension.swift in Sources */, 75C71A3C25E454460084187E /* OptimizelyClient+Extension.swift in Sources */, 75C71A3D25E454460084187E /* DataStoreQueueStackImpl+Extension.swift in Sources */, + 98CBECB72EB3CA6500C06A62 /* CmabCache.swift in Sources */, 845945BF2877589F00D13E11 /* OdpConfig.swift in Sources */, 75C71A3E25E454460084187E /* Array+Extension.swift in Sources */, 75C71A3F25E454460084187E /* Constants.swift in Sources */, @@ -6172,6 +6205,7 @@ 6EF8DE1C24BD1BB2008B9488 /* OptimizelyDecideOption.swift in Sources */, 980CC9132D833F2800E07D24 /* ExperimentCore.swift in Sources */, BD6485642491474500F30986 /* Rollout.swift in Sources */, + 98CBECB22EB3CA6500C06A62 /* CmabCache.swift in Sources */, BD6485652491474500F30986 /* DataStoreQueueStackImpl+Extension.swift in Sources */, BD6485662491474500F30986 /* OptimizelyJSON.swift in Sources */, 848617CA2863DC2700B7F41B /* OdpSegmentManager.swift in Sources */, diff --git a/Sources/CMAB/CmabCache.swift b/Sources/CMAB/CmabCache.swift new file mode 100644 index 00000000..e70a9cf3 --- /dev/null +++ b/Sources/CMAB/CmabCache.swift @@ -0,0 +1,26 @@ +// +// Copyright 2022, Optimizely, Inc. and contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +class CmabCache: LruCache { + override func lookup(key: String) -> CmabCacheValue? { + if timeoutInSecs <= 0 { + return nil + } + return super.lookup(key: key) + } +} diff --git a/Sources/CMAB/CmabService.swift b/Sources/CMAB/CmabService.swift index 09eb71c3..66375bf9 100644 --- a/Sources/CMAB/CmabService.swift +++ b/Sources/CMAB/CmabService.swift @@ -48,10 +48,10 @@ class DefaultCmabService: CmabService { typealias UserAttributes = [String : Any?] private let cmabClient: CmabClient - let cmabCache: LruCache + let cmabCache: CmabCache private let logger = OPTLoggerFactory.getLogger() - init(cmabClient: CmabClient, cmabCache: LruCache) { + init(cmabClient: CmabClient, cmabCache: CmabCache) { self.cmabClient = cmabClient self.cmabCache = cmabCache } @@ -181,12 +181,12 @@ class DefaultCmabService: CmabService { extension DefaultCmabService { static func createDefault() -> DefaultCmabService { - let cache = LruCache(size: DEFAULT_CMAB_CACHE_SIZE, timeoutInSecs: DEFAULT_CMAB_CACHE_TIMEOUT) + let cache = CmabCache(size: DEFAULT_CMAB_CACHE_SIZE, timeoutInSecs: DEFAULT_CMAB_CACHE_TIMEOUT) return DefaultCmabService(cmabClient: DefaultCmabClient(), cmabCache: cache) } static func createDefault(cacheSize: Int, cacheTimeout: Int) -> DefaultCmabService { - let cache = LruCache(size: cacheSize, timeoutInSecs: cacheTimeout) + let cache = CmabCache(size: cacheSize, timeoutInSecs: cacheTimeout) return DefaultCmabService(cmabClient: DefaultCmabClient(), cmabCache: cache) } } diff --git a/Sources/ODP/OptimizelySdkSettings.swift b/Sources/ODP/OptimizelySdkSettings.swift index 3065380e..2ba12c63 100644 --- a/Sources/ODP/OptimizelySdkSettings.swift +++ b/Sources/ODP/OptimizelySdkSettings.swift @@ -28,7 +28,7 @@ public struct OptimizelySdkSettings { /// The maximum size of cmab cache let cmabCacheSize: Int /// The timeout in seconds of cmab cache - let cmabCacheTitmeoutInSecs: Int + let cmabCacheTimeoutInSecs: Int /// ODP features are disabled if this is set to true. let disableOdp: Bool /// VUID is enabled if this is set to true. @@ -43,7 +43,7 @@ public struct OptimizelySdkSettings { /// - timeoutForSegmentFetchInSecs: The timeout in seconds of odp segment fetch (optional. default = 10) - OS default timeout will be used if this is set to zero. /// - timeoutForOdpEventInSecs: The timeout in seconds of odp event dispatch (optional. default = 10) - OS default timeout will be used if this is set to zero. /// - cmabCacheSize: The maximum size of cmab cache (optional. default = 100). Set to zero to disable caching. - /// - cmabCacheTitmeoutInSecs: The timeout in seconds of amb cache (optional. default = 600). Set to zero to disable timeout. + /// - cmabCacheTimeoutInSecs: The timeout in seconds of amb cache (optional. default = 600). Set to zero to disable timeout. /// - disableOdp: Set this flag to true (default = false) to disable ODP features /// - enableVuid: Set this flag to true (default = false) to enable vuid. /// - sdkName: Set this flag to override sdkName included in events @@ -53,7 +53,7 @@ public struct OptimizelySdkSettings { timeoutForSegmentFetchInSecs: Int = 10, timeoutForOdpEventInSecs: Int = 10, cmabCacheSize: Int = 100, - cmabCacheTitmeoutInSecs: Int = 600, + cmabCacheTimeoutInSecs: Int = 600, disableOdp: Bool = false, enableVuid: Bool = false, sdkName: String? = nil, @@ -61,7 +61,7 @@ public struct OptimizelySdkSettings { self.segmentsCacheSize = segmentsCacheSize self.segmentsCacheTimeoutInSecs = segmentsCacheTimeoutInSecs self.cmabCacheSize = cmabCacheSize - self.cmabCacheTitmeoutInSecs = cmabCacheTitmeoutInSecs + self.cmabCacheTimeoutInSecs = cmabCacheTimeoutInSecs self.timeoutForSegmentFetchInSecs = timeoutForSegmentFetchInSecs self.timeoutForOdpEventInSecs = timeoutForOdpEventInSecs self.disableOdp = disableOdp diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index 74d0af25..f3d4993f 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -108,7 +108,7 @@ open class OptimizelyClient: NSObject { let logger = logger ?? DefaultLogger() type(of: logger).logLevel = defaultLogLevel ?? .info - let cmabService = DefaultCmabService.createDefault(cacheSize: self.sdkSettings.cmabCacheSize, cacheTimeout: self.sdkSettings.cmabCacheTitmeoutInSecs) + let cmabService = DefaultCmabService.createDefault(cacheSize: self.sdkSettings.cmabCacheSize, cacheTimeout: self.sdkSettings.cmabCacheTimeoutInSecs) self.registerServices(sdkKey: sdkKey, logger: logger, diff --git a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift index dfe8f324..0c640ef4 100644 --- a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift +++ b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift @@ -51,7 +51,7 @@ class OptimizelyClientTests_ODP: XCTestCase { var sdkSettings = OptimizelySdkSettings(segmentsCacheSize: 12, segmentsCacheTimeoutInSecs: 345, cmabCacheSize: 50, - cmabCacheTitmeoutInSecs: 120) + cmabCacheTimeoutInSecs: 120) var optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: sdkSettings) let cmabCache = ((optimizely.decisionService as! DefaultDecisionService).cmabService as! DefaultCmabService).cmabCache XCTAssertEqual(50, cmabCache.maxSize) diff --git a/Tests/OptimizelyTests-Common/CmabServiceTests.swift b/Tests/OptimizelyTests-Common/CmabServiceTests.swift index f79d2f16..79b8dba0 100644 --- a/Tests/OptimizelyTests-Common/CmabServiceTests.swift +++ b/Tests/OptimizelyTests-Common/CmabServiceTests.swift @@ -111,7 +111,7 @@ class MockUserContext: OptimizelyUserContext { class DefaultCmabServiceTests: XCTestCase { fileprivate var cmabClient: MockCmabClient! fileprivate var config: MockProjectConfig! - var cmabCache: LruCache! + var cmabCache: CmabCache! var cmabService: DefaultCmabService! var userContext: OptimizelyUserContext! let userAttributes: [String: Any] = ["age": 25, "location": "San Francisco"] @@ -120,7 +120,7 @@ class DefaultCmabServiceTests: XCTestCase { super.setUp() config = MockProjectConfig() cmabClient = MockCmabClient() - cmabCache = LruCache(size: 10, timeoutInSecs: 10) + cmabCache = CmabCache(size: 10, timeoutInSecs: 10) cmabService = DefaultCmabService(cmabClient: cmabClient, cmabCache: cmabCache) // Set up user context userContext = MockUserContext(userId: "test-user", attributes: userAttributes) @@ -657,3 +657,220 @@ extension DefaultCmabServiceTests { } } + +extension DefaultCmabServiceTests { + + func testCacheSizeZero() { + // Create a cache with size 0 (no caching) + let zeroCmabCache = CmabCache(size: 0, timeoutInSecs: 10) + let zeroCacheService = DefaultCmabService(cmabClient: cmabClient, cmabCache: zeroCmabCache) + + cmabClient.fetchDecisionResult = .success("variation-first") + + let expectation1 = self.expectation(description: "first request") + + // First request + zeroCacheService.getDecision( + config: config, + userContext: userContext, + ruleId: "exp-123", + options: [] + ) { result in + switch result { + case .success(let decision): + XCTAssertEqual(decision.variationId, "variation-first") + XCTAssertTrue(self.cmabClient.fetchDecisionCalled, "Should call API on first request") + + case .failure(let error): + XCTFail("Expected success but got error: \(error)") + } + expectation1.fulfill() + } + + wait(for: [expectation1], timeout: 1.0) + + // Reset and change the variation + cmabClient.reset() + cmabClient.fetchDecisionResult = .success("variation-second") + + let expectation2 = self.expectation(description: "second request") + + // Second request - should NOT use cache (size = 0) + zeroCacheService.getDecision( + config: config, + userContext: userContext, + ruleId: "exp-123", + options: [] + ) { result in + switch result { + case .success(let decision): + XCTAssertEqual(decision.variationId, "variation-second") + XCTAssertTrue(self.cmabClient.fetchDecisionCalled, "Should call API again when cache size is 0") + + case .failure(let error): + XCTFail("Expected success but got error: \(error)") + } + expectation2.fulfill() + } + + wait(for: [expectation2], timeout: 1.0) + } + + func testCacheTimeoutZero() { + // Create a cache with timeout 0 (immediate expiration) + let zeroTimeoutCache = CmabCache(size: 10, timeoutInSecs: 0) + let zeroTimeoutService = DefaultCmabService(cmabClient: cmabClient, cmabCache: zeroTimeoutCache) + + cmabClient.fetchDecisionResult = .success("variation-first") + + let expectation1 = self.expectation(description: "first request") + + // First request + zeroTimeoutService.getDecision( + config: config, + userContext: userContext, + ruleId: "exp-123", + options: [] + ) { result in + switch result { + case .success(let decision): + XCTAssertEqual(decision.variationId, "variation-first") + XCTAssertTrue(self.cmabClient.fetchDecisionCalled, "Should call API on first request") + + case .failure(let error): + XCTFail("Expected success but got error: \(error)") + } + expectation1.fulfill() + } + + wait(for: [expectation1], timeout: 1.0) + + // Reset and change the variation + cmabClient.reset() + cmabClient.fetchDecisionResult = .success("variation-second") + + // Small delay to ensure cache timeout + Thread.sleep(forTimeInterval: 1.1) + + let expectation2 = self.expectation(description: "second request") + + // Second request - should NOT use cache (timeout = 0, cache expired) + zeroTimeoutService.getDecision( + config: config, + userContext: userContext, + ruleId: "exp-123", + options: [] + ) { result in + switch result { + case .success(let decision): + XCTAssertEqual(decision.variationId, "variation-second") + XCTAssertTrue(self.cmabClient.fetchDecisionCalled, "Should call API again when cache timeout is 0") + + case .failure(let error): + XCTFail("Expected success but got error: \(error)") + } + expectation2.fulfill() + } + + wait(for: [expectation2], timeout: 1.0) + } + + func testCacheSizeZeroAndTimeoutZero() { + // Create a cache with both size 0 and timeout 0 (completely disabled) + let disabledCache = CmabCache(size: 0, timeoutInSecs: 0) + let disabledCacheService = DefaultCmabService(cmabClient: cmabClient, cmabCache: disabledCache) + + cmabClient.fetchDecisionResult = .success("variation-1") + + let expectation1 = self.expectation(description: "first request") + + // First request + disabledCacheService.getDecision( + config: config, + userContext: userContext, + ruleId: "exp-123", + options: [] + ) { result in + switch result { + case .success(let decision): + XCTAssertEqual(decision.variationId, "variation-1") + + case .failure(let error): + XCTFail("Expected success but got error: \(error)") + } + expectation1.fulfill() + } + + wait(for: [expectation1], timeout: 1.0) + + let apiCallCount1 = cmabClient.fetchDecisionCalled ? 1 : 0 + + // Reset and make multiple requests + cmabClient.reset() + cmabClient.fetchDecisionResult = .success("variation-2") + + let expectation2 = self.expectation(description: "second request") + + disabledCacheService.getDecision( + config: config, + userContext: userContext, + ruleId: "exp-123", + options: [] + ) { result in + expectation2.fulfill() + } + + wait(for: [expectation2], timeout: 1.0) + + let apiCallCount2 = cmabClient.fetchDecisionCalled ? 1 : 0 + + XCTAssertEqual(apiCallCount1, 1, "First request should call API") + XCTAssertEqual(apiCallCount2, 1, "Second request should also call API (no caching)") + } + + func testSyncCacheSizeZero() { + // Create a cache with size 0 + let zeroCmabCache = CmabCache(size: 0, timeoutInSecs: 10) + let zeroCacheService = DefaultCmabService(cmabClient: cmabClient, cmabCache: zeroCmabCache) + + cmabClient.fetchDecisionResult = .success("variation-first") + + // First request + let result1 = zeroCacheService.getDecision( + config: config, + userContext: userContext, + ruleId: "exp-123", + options: [] + ) + + switch result1 { + case .success(let decision): + XCTAssertEqual(decision.variationId, "variation-first") + XCTAssertTrue(cmabClient.fetchDecisionCalled, "Should call API on first request") + + case .failure(let error): + XCTFail("Expected success but got error: \(error)") + } + + // Reset and change variation + cmabClient.reset() + cmabClient.fetchDecisionResult = .success("variation-second") + + // Second request - should NOT use cache + let result2 = zeroCacheService.getDecision( + config: config, + userContext: userContext, + ruleId: "exp-123", + options: [] + ) + + switch result2 { + case .success(let decision): + XCTAssertEqual(decision.variationId, "variation-second") + XCTAssertTrue(cmabClient.fetchDecisionCalled, "Should call API again when cache size is 0") + + case .failure(let error): + XCTFail("Expected success but got error: \(error)") + } + } +} diff --git a/Tests/OptimizelyTests-Common/DecisionServiceTests_Experiments.swift b/Tests/OptimizelyTests-Common/DecisionServiceTests_Experiments.swift index 80bd62cc..8217002a 100644 --- a/Tests/OptimizelyTests-Common/DecisionServiceTests_Experiments.swift +++ b/Tests/OptimizelyTests-Common/DecisionServiceTests_Experiments.swift @@ -887,7 +887,7 @@ fileprivate class MockCmabService: DefaultCmabService { var variationId: String? init() { - super.init(cmabClient: DefaultCmabClient(), cmabCache: LruCache(size: 10, timeoutInSecs: 10)) + super.init(cmabClient: DefaultCmabClient(), cmabCache: CmabCache(size: 10, timeoutInSecs: 10)) } override func getDecision(config: ProjectConfig, userContext: OptimizelyUserContext, ruleId: String, options: [OptimizelyDecideOption]) -> Result { diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Async.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Async.swift index 70dc3d6b..8d678722 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Async.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Async.swift @@ -485,7 +485,7 @@ fileprivate class MockCmabService: DefaultCmabService { var ignoreCacheUsed = false init() { - super.init(cmabClient: DefaultCmabClient(), cmabCache: LruCache(size: 10, timeoutInSecs: 10)) + super.init(cmabClient: DefaultCmabClient(), cmabCache: CmabCache(size: 10, timeoutInSecs: 10)) } override func getDecision(config: ProjectConfig, userContext: OptimizelyUserContext, ruleId: String, options: [OptimizelyDecideOption]) -> Result { diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_CMAB.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_CMAB.swift index e9046953..84fa2401 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_CMAB.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_CMAB.swift @@ -284,7 +284,7 @@ fileprivate class MockCmabService: DefaultCmabService { var invalidateUserCmabCache = false init() { - super.init(cmabClient: DefaultCmabClient(), cmabCache: LruCache(size: 10, timeoutInSecs: 10)) + super.init(cmabClient: DefaultCmabClient(), cmabCache: CmabCache(size: 10, timeoutInSecs: 10)) } override func getDecision(config: ProjectConfig, userContext: OptimizelyUserContext, ruleId: String, options: [OptimizelyDecideOption]) -> Result { From a701d0c804ceee27d256045473a9dfaa5496ac95 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Thu, 30 Oct 2025 22:53:52 +0600 Subject: [PATCH 4/7] fix: fix cmab cache descriptions in OptimizelySdkSettings.swift --- Sources/ODP/OptimizelySdkSettings.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/ODP/OptimizelySdkSettings.swift b/Sources/ODP/OptimizelySdkSettings.swift index 2ba12c63..02fdf184 100644 --- a/Sources/ODP/OptimizelySdkSettings.swift +++ b/Sources/ODP/OptimizelySdkSettings.swift @@ -42,8 +42,8 @@ public struct OptimizelySdkSettings { /// - segmentsCacheTimeoutInSecs: The timeout in seconds of audience segments cache (optional. default = 600). Set to zero to disable timeout. /// - timeoutForSegmentFetchInSecs: The timeout in seconds of odp segment fetch (optional. default = 10) - OS default timeout will be used if this is set to zero. /// - timeoutForOdpEventInSecs: The timeout in seconds of odp event dispatch (optional. default = 10) - OS default timeout will be used if this is set to zero. - /// - cmabCacheSize: The maximum size of cmab cache (optional. default = 100). Set to zero to disable caching. - /// - cmabCacheTimeoutInSecs: The timeout in seconds of amb cache (optional. default = 600). Set to zero to disable timeout. + /// - cmabCacheSize: The maximum size of cmab cache (optional. default = 100). + /// - cmabCacheTimeoutInSecs: The timeout in seconds of amb cache (optional. default = 600). /// - disableOdp: Set this flag to true (default = false) to disable ODP features /// - enableVuid: Set this flag to true (default = false) to enable vuid. /// - sdkName: Set this flag to override sdkName included in events From 75efa1dd23c906e5a8f5d4241985545a5113d92a Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Thu, 30 Oct 2025 23:15:13 +0600 Subject: [PATCH 5/7] chore: update default CMAB cache timeout to 30 minutes --- Sources/CMAB/CmabCache.swift | 3 +++ Sources/CMAB/CmabService.swift | 3 --- Sources/ODP/OptimizelySdkSettings.swift | 5 ++--- Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Sources/CMAB/CmabCache.swift b/Sources/CMAB/CmabCache.swift index e70a9cf3..cbf53a16 100644 --- a/Sources/CMAB/CmabCache.swift +++ b/Sources/CMAB/CmabCache.swift @@ -16,6 +16,9 @@ import Foundation +let DEFAULT_CMAB_CACHE_TIMEOUT = 30 * 60 // 30 minutes +let DEFAULT_CMAB_CACHE_SIZE = 100 + class CmabCache: LruCache { override func lookup(key: String) -> CmabCacheValue? { if timeoutInSecs <= 0 { diff --git a/Sources/CMAB/CmabService.swift b/Sources/CMAB/CmabService.swift index 66375bf9..8b13c0c7 100644 --- a/Sources/CMAB/CmabService.swift +++ b/Sources/CMAB/CmabService.swift @@ -41,9 +41,6 @@ protocol CmabService { completion: @escaping CmabDecisionCompletionHandler) } -let DEFAULT_CMAB_CACHE_TIMEOUT = 600 // secs -let DEFAULT_CMAB_CACHE_SIZE = 100 - class DefaultCmabService: CmabService { typealias UserAttributes = [String : Any?] diff --git a/Sources/ODP/OptimizelySdkSettings.swift b/Sources/ODP/OptimizelySdkSettings.swift index 02fdf184..5887e372 100644 --- a/Sources/ODP/OptimizelySdkSettings.swift +++ b/Sources/ODP/OptimizelySdkSettings.swift @@ -34,7 +34,6 @@ public struct OptimizelySdkSettings { /// VUID is enabled if this is set to true. let enableVuid: Bool - /// Optimizely SDK Settings /// /// - Parameters: @@ -43,7 +42,7 @@ public struct OptimizelySdkSettings { /// - timeoutForSegmentFetchInSecs: The timeout in seconds of odp segment fetch (optional. default = 10) - OS default timeout will be used if this is set to zero. /// - timeoutForOdpEventInSecs: The timeout in seconds of odp event dispatch (optional. default = 10) - OS default timeout will be used if this is set to zero. /// - cmabCacheSize: The maximum size of cmab cache (optional. default = 100). - /// - cmabCacheTimeoutInSecs: The timeout in seconds of amb cache (optional. default = 600). + /// - cmabCacheTimeoutInSecs: The timeout in seconds of amb cache (optional. default = 30 * 60). /// - disableOdp: Set this flag to true (default = false) to disable ODP features /// - enableVuid: Set this flag to true (default = false) to enable vuid. /// - sdkName: Set this flag to override sdkName included in events @@ -53,7 +52,7 @@ public struct OptimizelySdkSettings { timeoutForSegmentFetchInSecs: Int = 10, timeoutForOdpEventInSecs: Int = 10, cmabCacheSize: Int = 100, - cmabCacheTimeoutInSecs: Int = 600, + cmabCacheTimeoutInSecs: Int = 30 * 60, disableOdp: Bool = false, enableVuid: Bool = false, sdkName: String? = nil, diff --git a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift index 0c640ef4..516dca5d 100644 --- a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift +++ b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift @@ -39,7 +39,7 @@ class OptimizelyClientTests_ODP: XCTestCase { let optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey) let cmabCache = ((optimizely.decisionService as! DefaultDecisionService).cmabService as! DefaultCmabService).cmabCache XCTAssertEqual(100, cmabCache.maxSize) - XCTAssertEqual(600, cmabCache.timeoutInSecs) + XCTAssertEqual(30 * 60, cmabCache.timeoutInSecs) XCTAssertEqual(100, optimizely.odpManager.segmentManager?.segmentsCache.maxSize) XCTAssertEqual(600, optimizely.odpManager.segmentManager?.segmentsCache.timeoutInSecs) XCTAssertEqual(10, optimizely.odpManager.segmentManager?.apiMgr.resourceTimeoutInSecs) From d3e06629176d1ef120cdc7e52499822ffe3c933b Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Mon, 3 Nov 2025 20:26:42 +0600 Subject: [PATCH 6/7] refactor: adjust CmabCache inheritance to typealias and update cache timeout calculation --- Sources/CMAB/CmabCache.swift | 9 +---- Sources/CMAB/CmabService.swift | 4 +- .../OptimizelyClientTests_ODP.swift | 9 ++++- .../CmabServiceTests.swift | 38 ++++++++++--------- 4 files changed, 32 insertions(+), 28 deletions(-) diff --git a/Sources/CMAB/CmabCache.swift b/Sources/CMAB/CmabCache.swift index cbf53a16..f22fadd9 100644 --- a/Sources/CMAB/CmabCache.swift +++ b/Sources/CMAB/CmabCache.swift @@ -19,11 +19,4 @@ import Foundation let DEFAULT_CMAB_CACHE_TIMEOUT = 30 * 60 // 30 minutes let DEFAULT_CMAB_CACHE_SIZE = 100 -class CmabCache: LruCache { - override func lookup(key: String) -> CmabCacheValue? { - if timeoutInSecs <= 0 { - return nil - } - return super.lookup(key: key) - } -} +typealias CmabCache = LruCache diff --git a/Sources/CMAB/CmabService.swift b/Sources/CMAB/CmabService.swift index 5dd44b04..65c7d5b4 100644 --- a/Sources/CMAB/CmabService.swift +++ b/Sources/CMAB/CmabService.swift @@ -198,7 +198,9 @@ extension DefaultCmabService { } static func createDefault(cacheSize: Int, cacheTimeout: Int) -> DefaultCmabService { - let cache = CmabCache(size: cacheSize, timeoutInSecs: cacheTimeout) + // if cache timeout is set to 0 or negative, use default timeout + let timeout = cacheTimeout <= 0 ? DEFAULT_CMAB_CACHE_TIMEOUT : cacheTimeout + let cache = CmabCache(size: cacheSize, timeoutInSecs: timeout) return DefaultCmabService(cmabClient: DefaultCmabClient(), cmabCache: cache) } } diff --git a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift index 516dca5d..4ee7b054 100644 --- a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift +++ b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift @@ -53,7 +53,7 @@ class OptimizelyClientTests_ODP: XCTestCase { cmabCacheSize: 50, cmabCacheTimeoutInSecs: 120) var optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: sdkSettings) - let cmabCache = ((optimizely.decisionService as! DefaultDecisionService).cmabService as! DefaultCmabService).cmabCache + var cmabCache = ((optimizely.decisionService as! DefaultDecisionService).cmabService as! DefaultCmabService).cmabCache XCTAssertEqual(50, cmabCache.maxSize) XCTAssertEqual(120, cmabCache.timeoutInSecs) XCTAssertEqual(12, optimizely.odpManager.segmentManager?.segmentsCache.maxSize) @@ -64,6 +64,13 @@ class OptimizelyClientTests_ODP: XCTestCase { optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: sdkSettings) XCTAssertEqual(34, optimizely.odpManager.segmentManager?.apiMgr.resourceTimeoutInSecs) XCTAssertEqual(45, optimizely.odpManager.eventManager?.apiMgr.resourceTimeoutInSecs) + + sdkSettings = OptimizelySdkSettings(cmabCacheSize: 50, cmabCacheTimeoutInSecs: -10) + + optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: sdkSettings) + cmabCache = ((optimizely.decisionService as! DefaultDecisionService).cmabService as! DefaultCmabService).cmabCache + XCTAssertEqual(50, cmabCache.maxSize) + XCTAssertEqual(1800, cmabCache.timeoutInSecs) sdkSettings = OptimizelySdkSettings(disableOdp: true) optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: sdkSettings) diff --git a/Tests/OptimizelyTests-Common/CmabServiceTests.swift b/Tests/OptimizelyTests-Common/CmabServiceTests.swift index 79b8dba0..bca742ce 100644 --- a/Tests/OptimizelyTests-Common/CmabServiceTests.swift +++ b/Tests/OptimizelyTests-Common/CmabServiceTests.swift @@ -717,7 +717,8 @@ extension DefaultCmabServiceTests { } func testCacheTimeoutZero() { - // Create a cache with timeout 0 (immediate expiration) + // When timeout is 0, LruCache uses default timeout (30 * 60 seconds = 1800 seconds) + // So cache will NOT expire within the test timeframe let zeroTimeoutCache = CmabCache(size: 10, timeoutInSecs: 0) let zeroTimeoutService = DefaultCmabService(cmabClient: cmabClient, cmabCache: zeroTimeoutCache) @@ -733,28 +734,28 @@ extension DefaultCmabServiceTests { options: [] ) { result in switch result { - case .success(let decision): - XCTAssertEqual(decision.variationId, "variation-first") - XCTAssertTrue(self.cmabClient.fetchDecisionCalled, "Should call API on first request") - - case .failure(let error): - XCTFail("Expected success but got error: \(error)") + case .success(let decision): + XCTAssertEqual(decision.variationId, "variation-first") + XCTAssertTrue(self.cmabClient.fetchDecisionCalled, "Should call API on first request") + + case .failure(let error): + XCTFail("Expected success but got error: \(error)") } expectation1.fulfill() } wait(for: [expectation1], timeout: 1.0) - // Reset and change the variation + // Reset client but don't change the result cmabClient.reset() cmabClient.fetchDecisionResult = .success("variation-second") - // Small delay to ensure cache timeout - Thread.sleep(forTimeInterval: 1.1) + // Small delay (but not enough to expire default 1800s timeout) + Thread.sleep(forTimeInterval: 0.1) let expectation2 = self.expectation(description: "second request") - // Second request - should NOT use cache (timeout = 0, cache expired) + // Second request - SHOULD use cache (timeout defaults to 1800s, not expired) zeroTimeoutService.getDecision( config: config, userContext: userContext, @@ -762,19 +763,20 @@ extension DefaultCmabServiceTests { options: [] ) { result in switch result { - case .success(let decision): - XCTAssertEqual(decision.variationId, "variation-second") - XCTAssertTrue(self.cmabClient.fetchDecisionCalled, "Should call API again when cache timeout is 0") - - case .failure(let error): - XCTFail("Expected success but got error: \(error)") + case .success(let decision): + // Should get cached value, not the new one + XCTAssertEqual(decision.variationId, "variation-first") + XCTAssertFalse(self.cmabClient.fetchDecisionCalled, "Should use cache when timeout defaults to 1800s") + + case .failure(let error): + XCTFail("Expected success but got error: \(error)") } expectation2.fulfill() } wait(for: [expectation2], timeout: 1.0) } - + func testCacheSizeZeroAndTimeoutZero() { // Create a cache with both size 0 and timeout 0 (completely disabled) let disabledCache = CmabCache(size: 0, timeoutInSecs: 0) From ebd8c27a132cd004ad95b25ad9f5a2e4ae99e676 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Mon, 3 Nov 2025 22:03:12 +0600 Subject: [PATCH 7/7] refactor: remove unused CmabCache.swift and update references to DEFAULT_CMAB_CACHE_TIMEOUT and DEFAULT_CMAB_CACHE_SIZE --- OptimizelySwiftSDK.xcodeproj/project.pbxproj | 34 -------------------- Sources/CMAB/CmabCache.swift | 22 ------------- Sources/CMAB/CmabService.swift | 5 +++ 3 files changed, 5 insertions(+), 56 deletions(-) delete mode 100644 Sources/CMAB/CmabCache.swift diff --git a/OptimizelySwiftSDK.xcodeproj/project.pbxproj b/OptimizelySwiftSDK.xcodeproj/project.pbxproj index 0231ec92..a442597c 100644 --- a/OptimizelySwiftSDK.xcodeproj/project.pbxproj +++ b/OptimizelySwiftSDK.xcodeproj/project.pbxproj @@ -2078,22 +2078,6 @@ 98AC984B2DB8FFE0001405DD /* DecisionServiceTests_Holdouts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98AC98482DB8FC29001405DD /* DecisionServiceTests_Holdouts.swift */; }; 98AC985E2DBA6721001405DD /* OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98AC985D2DBA6721001405DD /* OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift */; }; 98AC985F2DBA6721001405DD /* OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98AC985D2DBA6721001405DD /* OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift */; }; - 98CBECAC2EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; - 98CBECAD2EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; - 98CBECAE2EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; - 98CBECAF2EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; - 98CBECB02EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; - 98CBECB12EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; - 98CBECB22EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; - 98CBECB32EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; - 98CBECB42EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; - 98CBECB52EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; - 98CBECB62EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; - 98CBECB72EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; - 98CBECB82EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; - 98CBECB92EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; - 98CBECBA2EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; - 98CBECBB2EB3CA6500C06A62 /* CmabCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */; }; 98D5AE842DBB91C0000D5844 /* OptimizelyUserContextTests_Decide_Holdouts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D5AE832DBB91C0000D5844 /* OptimizelyUserContextTests_Decide_Holdouts.swift */; }; 98D5AE852DBB91C0000D5844 /* OptimizelyUserContextTests_Decide_Holdouts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D5AE832DBB91C0000D5844 /* OptimizelyUserContextTests_Decide_Holdouts.swift */; }; 98F28A1D2E01940500A86546 /* Cmab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98F28A1C2E01940500A86546 /* Cmab.swift */; }; @@ -2603,7 +2587,6 @@ 98AC98452DB7B762001405DD /* BucketTests_HoldoutToVariation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BucketTests_HoldoutToVariation.swift; sourceTree = ""; }; 98AC98482DB8FC29001405DD /* DecisionServiceTests_Holdouts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecisionServiceTests_Holdouts.swift; sourceTree = ""; }; 98AC985D2DBA6721001405DD /* OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift; sourceTree = ""; }; - 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CmabCache.swift; sourceTree = ""; }; 98D5AE832DBB91C0000D5844 /* OptimizelyUserContextTests_Decide_Holdouts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptimizelyUserContextTests_Decide_Holdouts.swift; sourceTree = ""; }; 98F28A1C2E01940500A86546 /* Cmab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cmab.swift; sourceTree = ""; }; 98F28A2D2E01968000A86546 /* CmabTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CmabTests.swift; sourceTree = ""; }; @@ -3321,7 +3304,6 @@ children = ( 98F28A402E02DD6D00A86546 /* CmabClient.swift */, 98F28A552E0451CC00A86546 /* CmabService.swift */, - 98CBECAB2EB3CA6500C06A62 /* CmabCache.swift */, ); path = CMAB; sourceTree = ""; @@ -4339,7 +4321,6 @@ 989428BB2DBFA431008BA1C8 /* MockBucketer.swift in Sources */, 6E14CDAB2423F9EB00010234 /* MockUrlSession.swift in Sources */, 6E14CDAA2423F9C300010234 /* SDKVersion.swift in Sources */, - 98CBECB32EB3CA6500C06A62 /* CmabCache.swift in Sources */, 845945C3287758A100D13E11 /* OdpConfig.swift in Sources */, 6E14CD832423F9A100010234 /* DataStoreQueueStackImpl.swift in Sources */, 848617F12863E21200B7F41B /* OdpEventApiManager.swift in Sources */, @@ -4494,7 +4475,6 @@ 6E424D0926324B620081004A /* FeatureVariable.swift in Sources */, 6E6419DA2657059700C49555 /* NotificationCenterTests_MultiClients.swift in Sources */, 6E424D0A26324B620081004A /* Rollout.swift in Sources */, - 98CBECB52EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E2D5DAE26338CA00002077F /* AtomicDictionaryTests.swift in Sources */, 6E424D0B26324B620081004A /* Variation.swift in Sources */, 6E424D0C26324B620081004A /* TrafficAllocation.swift in Sources */, @@ -4626,7 +4606,6 @@ 6EF8DE1B24BD1BB2008B9488 /* OptimizelyDecideOption.swift in Sources */, 980CC90D2D833F2800E07D24 /* ExperimentCore.swift in Sources */, 6E75173722C520D400B2B157 /* MurmurHash3.swift in Sources */, - 98CBECB42EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E7517F922C520D400B2B157 /* DataStoreUserDefaults.swift in Sources */, C78CAF592445AD8D009FE876 /* OptimizelyJSON.swift in Sources */, 848617C92863DC2700B7F41B /* OdpSegmentManager.swift in Sources */, @@ -4669,7 +4648,6 @@ 989428B32DBFA431008BA1C8 /* MockBucketer.swift in Sources */, 6E75170222C520D400B2B157 /* OptimizelyLogLevel.swift in Sources */, 6E7516BA22C520D400B2B157 /* DefaultUserProfileService.swift in Sources */, - 98CBECB02EB3CA6500C06A62 /* CmabCache.swift in Sources */, 845945C7287758A300D13E11 /* OdpConfig.swift in Sources */, 6E75175622C520D400B2B157 /* LogMessage.swift in Sources */, 848617F62863E21200B7F41B /* OdpEventApiManager.swift in Sources */, @@ -4888,7 +4866,6 @@ 6E75175E22C520D400B2B157 /* AtomicProperty.swift in Sources */, 6E9B11DE22C548A200C22D81 /* OptimizelyClientTests_Others.swift in Sources */, C78CAF7424482C86009FE876 /* OptimizelyClientTests_OptimizelyJSON.swift in Sources */, - 98CBECBA2EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6EC6DD3724ABF6990017D296 /* OptimizelyClient+Decide.swift in Sources */, 6E7516E622C520D400B2B157 /* OPTEventDispatcher.swift in Sources */, 98F28A612E0451CC00A86546 /* CmabService.swift in Sources */, @@ -4948,7 +4925,6 @@ 6E75173D22C520D400B2B157 /* MurmurHash3.swift in Sources */, 6E7516E922C520D400B2B157 /* OPTEventDispatcher.swift in Sources */, 6E7518A722C520D400B2B157 /* FeatureFlag.swift in Sources */, - 98CBECB92EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E75187722C520D400B2B157 /* Variation.swift in Sources */, 6E7517F322C520D400B2B157 /* DataStoreMemory.swift in Sources */, 6E7518FB22C520D500B2B157 /* UserAttribute.swift in Sources */, @@ -5119,7 +5095,6 @@ 84E2E94F2852A378001114AB /* VuidManager.swift in Sources */, 6E75176322C520D400B2B157 /* AtomicProperty.swift in Sources */, 6E9B117722C5487100C22D81 /* BatchEventBuilderTests_EventTags.swift in Sources */, - 98CBECB62EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E7517DD22C520D400B2B157 /* DefaultNotificationCenter.swift in Sources */, 6E9B116622C5487100C22D81 /* DecisionServiceTests_UserProfiles.swift in Sources */, 6E34A6202319EBB800BAE302 /* Notifications.swift in Sources */, @@ -5277,7 +5252,6 @@ 6E9B11B822C5489600C22D81 /* OTUtils.swift in Sources */, 98AC97F42DAE9685001405DD /* HoldoutConfigTests.swift in Sources */, 6E9B119022C5488300C22D81 /* AttributeValueTests.swift in Sources */, - 98CBECBB2EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E994B4025A3E6EA00999262 /* DecisionResponse.swift in Sources */, 84E2E9802855875E001114AB /* OdpEventManager.swift in Sources */, 6E75175822C520D400B2B157 /* LogMessage.swift in Sources */, @@ -5416,7 +5390,6 @@ 6E4544AF270E67C800F2CEBC /* NetworkReachability.swift in Sources */, 6E75177522C520D400B2B157 /* SDKVersion.swift in Sources */, 848617CD2863DC2700B7F41B /* OdpSegmentManager.swift in Sources */, - 98CBECAE2EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E75180722C520D400B2B157 /* DataStoreFile.swift in Sources */, 8486181D286D188B00B7F41B /* OdpEventApiManagerTests.swift in Sources */, 6E75183722C520D400B2B157 /* EventForDispatch.swift in Sources */, @@ -5599,7 +5572,6 @@ 6E424C04263228FD0081004A /* AtomicDictionary.swift in Sources */, 6E75189922C520D400B2B157 /* Experiment.swift in Sources */, 6E75178322C520D400B2B157 /* ArrayEventForDispatch+Extension.swift in Sources */, - 98CBECB12EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E424BE5263228E90081004A /* AtomicArray.swift in Sources */, 6E7518A522C520D400B2B157 /* FeatureFlag.swift in Sources */, ); @@ -5666,7 +5638,6 @@ 6E4544B4270E67C800F2CEBC /* NetworkReachability.swift in Sources */, 6E75184622C520D400B2B157 /* Event.swift in Sources */, 6E7517CE22C520D400B2B157 /* DefaultBucketer.swift in Sources */, - 98CBECB82EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E75180A22C520D400B2B157 /* DataStoreFile.swift in Sources */, 6E7516B822C520D400B2B157 /* DefaultUserProfileService.swift in Sources */, 6E7517C222C520D400B2B157 /* DefaultDatafileHandler.swift in Sources */, @@ -5776,7 +5747,6 @@ 6E4544B9270E67C800F2CEBC /* NetworkReachability.swift in Sources */, 6E75184B22C520D400B2B157 /* Event.swift in Sources */, 6E7517D322C520D400B2B157 /* DefaultBucketer.swift in Sources */, - 98CBECAD2EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E75180F22C520D400B2B157 /* DataStoreFile.swift in Sources */, 6E7516BD22C520D400B2B157 /* DefaultUserProfileService.swift in Sources */, 6E7517C722C520D400B2B157 /* DefaultDatafileHandler.swift in Sources */, @@ -5891,7 +5861,6 @@ 6EF8DE1A24BD1BB2008B9488 /* OptimizelyDecideOption.swift in Sources */, 980CC90B2D833F2800E07D24 /* ExperimentCore.swift in Sources */, 6E75186422C520D400B2B157 /* Rollout.swift in Sources */, - 98CBECAF2EB3CA6500C06A62 /* CmabCache.swift in Sources */, 6E75179622C520D400B2B157 /* DataStoreQueueStackImpl+Extension.swift in Sources */, C78CAF582445AD8D009FE876 /* OptimizelyJSON.swift in Sources */, 848617C82863DC2700B7F41B /* OdpSegmentManager.swift in Sources */, @@ -5934,7 +5903,6 @@ 989428BE2DBFA431008BA1C8 /* MockBucketer.swift in Sources */, 6E7516FC22C520D400B2B157 /* OptimizelyLogLevel.swift in Sources */, 6E7516B422C520D400B2B157 /* DefaultUserProfileService.swift in Sources */, - 98CBECAC2EB3CA6500C06A62 /* CmabCache.swift in Sources */, 845945C02877589F00D13E11 /* OdpConfig.swift in Sources */, 6E75175022C520D400B2B157 /* LogMessage.swift in Sources */, 848617EE2863E21200B7F41B /* OdpEventApiManager.swift in Sources */, @@ -6121,7 +6089,6 @@ 75C71A3B25E454460084187E /* ArrayEventForDispatch+Extension.swift in Sources */, 75C71A3C25E454460084187E /* OptimizelyClient+Extension.swift in Sources */, 75C71A3D25E454460084187E /* DataStoreQueueStackImpl+Extension.swift in Sources */, - 98CBECB72EB3CA6500C06A62 /* CmabCache.swift in Sources */, 845945BF2877589F00D13E11 /* OdpConfig.swift in Sources */, 75C71A3E25E454460084187E /* Array+Extension.swift in Sources */, 75C71A3F25E454460084187E /* Constants.swift in Sources */, @@ -6205,7 +6172,6 @@ 6EF8DE1C24BD1BB2008B9488 /* OptimizelyDecideOption.swift in Sources */, 980CC9132D833F2800E07D24 /* ExperimentCore.swift in Sources */, BD6485642491474500F30986 /* Rollout.swift in Sources */, - 98CBECB22EB3CA6500C06A62 /* CmabCache.swift in Sources */, BD6485652491474500F30986 /* DataStoreQueueStackImpl+Extension.swift in Sources */, BD6485662491474500F30986 /* OptimizelyJSON.swift in Sources */, 848617CA2863DC2700B7F41B /* OdpSegmentManager.swift in Sources */, diff --git a/Sources/CMAB/CmabCache.swift b/Sources/CMAB/CmabCache.swift deleted file mode 100644 index f22fadd9..00000000 --- a/Sources/CMAB/CmabCache.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright 2022, Optimizely, Inc. and contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -let DEFAULT_CMAB_CACHE_TIMEOUT = 30 * 60 // 30 minutes -let DEFAULT_CMAB_CACHE_SIZE = 100 - -typealias CmabCache = LruCache diff --git a/Sources/CMAB/CmabService.swift b/Sources/CMAB/CmabService.swift index 65c7d5b4..733bc0bf 100644 --- a/Sources/CMAB/CmabService.swift +++ b/Sources/CMAB/CmabService.swift @@ -41,6 +41,11 @@ protocol CmabService { completion: @escaping CmabDecisionCompletionHandler) } +let DEFAULT_CMAB_CACHE_TIMEOUT = 30 * 60 // 30 minutes +let DEFAULT_CMAB_CACHE_SIZE = 100 + +typealias CmabCache = LruCache + class DefaultCmabService: CmabService { typealias UserAttributes = [String : Any?]