From 520123c2d0da7cba9e80b5591f03398abd450cec Mon Sep 17 00:00:00 2001 From: Brian Giori Date: Mon, 25 Nov 2024 10:01:12 -0800 Subject: [PATCH] feat: increase flag polling interval; add flag poller interval config (#64) --- Cartfile.resolved | 2 +- Sources/Experiment/ExperimentClient.swift | 4 ++++ Sources/Experiment/ExperimentConfig.swift | 20 +++++++++++++++++++ .../ExperimentClientTests.swift | 18 +++++++++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index 7137d18..2134c4f 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1 +1 @@ -github "amplitude/analytics-connector-ios" "v1.0.3" +github "amplitude/analytics-connector-ios" "v1.3.0" diff --git a/Sources/Experiment/ExperimentClient.swift b/Sources/Experiment/ExperimentClient.swift index 2e66b88..6097556 100644 --- a/Sources/Experiment/ExperimentClient.swift +++ b/Sources/Experiment/ExperimentClient.swift @@ -31,6 +31,7 @@ private let fetchBackoffAttempts = 8 private let fetchBackoffMinMillis = 500 private let fetchBackoffMaxMillis = 10000 private let fetchBackoffScalar: Float = 1.5 +private let minFlagConfigPollingIntervalMillis = 60000 private let euServerUrl = "https://api.lab.eu.amplitude.com"; private let euFlagsServerUrl = "https://flag.lab.eu.amplitude.com"; @@ -72,6 +73,9 @@ internal class DefaultExperimentClient : NSObject, ExperimentClient { if config.serverUrl == ExperimentConfig.Defaults.serverUrl && config.flagsServerUrl == ExperimentConfig.Defaults.flagsServerUrl && config.serverZone == .EU { configBuilder.serverUrl(euServerUrl).flagsServerUrl(euFlagsServerUrl) } + if config.flagConfigPollingIntervalMillis < minFlagConfigPollingIntervalMillis { + configBuilder.flagConfigPollingIntervalMillis(minFlagConfigPollingIntervalMillis) + } self.config = configBuilder.build() if config.userProvider != nil { self.userProvider = config.userProvider diff --git a/Sources/Experiment/ExperimentConfig.swift b/Sources/Experiment/ExperimentConfig.swift index ff8512e..1e29e88 100644 --- a/Sources/Experiment/ExperimentConfig.swift +++ b/Sources/Experiment/ExperimentConfig.swift @@ -33,6 +33,7 @@ import Foundation @objc public let automaticExposureTracking: Bool @objc public let fetchOnStart: NSNumber? // objc cant do nil boolean values, use nsnumber @objc public let pollOnStart: Bool + @objc public let flagConfigPollingIntervalMillis: Int @objc public let automaticFetchOnAmplitudeIdentityChange: Bool @objc public let userProvider: ExperimentUserProvider? @available(*, deprecated, message: "Use exposureTrackingProvider instead.") @@ -54,6 +55,7 @@ import Foundation self.automaticExposureTracking = ExperimentConfig.Defaults.automaticExposureTracking self.fetchOnStart = ExperimentConfig.Defaults.fetchOnStart self.pollOnStart = ExperimentConfig.Defaults.pollOnStart + self.flagConfigPollingIntervalMillis = ExperimentConfig.Defaults.flagConfigPollingIntervalMillis self.automaticFetchOnAmplitudeIdentityChange = ExperimentConfig.Defaults.automaticFetchOnAmplitudeIdentityChange self.userProvider = ExperimentConfig.Defaults.userProvider self.analyticsProvider = ExperimentConfig.Defaults.analyticsProvider @@ -75,6 +77,7 @@ import Foundation self.automaticExposureTracking = builder.automaticExposureTracking self.fetchOnStart = builder.fetchOnStart self.pollOnStart = builder.pollOnStart + self.flagConfigPollingIntervalMillis = builder.flagConfigPollingIntervalMillis self.automaticFetchOnAmplitudeIdentityChange = builder.automaticFetchOnAmplitudeIdentityChange self.userProvider = builder.userProvider self.analyticsProvider = builder.analyticsProvider @@ -96,6 +99,7 @@ import Foundation self.automaticExposureTracking = builder.automaticExposureTracking self.fetchOnStart = builder.fetchOnStart self.pollOnStart = builder.pollOnStart + self.flagConfigPollingIntervalMillis = builder.flagConfigPollingIntervalMillis self.automaticFetchOnAmplitudeIdentityChange = builder.automaticFetchOnAmplitudeIdentityChange self.userProvider = builder.userProvider self.analyticsProvider = builder.analyticsProvider @@ -117,6 +121,7 @@ import Foundation static let automaticExposureTracking: Bool = true static let fetchOnStart: NSNumber? = 1 static let pollOnStart: Bool = true + static let flagConfigPollingIntervalMillis = 300000 static let automaticFetchOnAmplitudeIdentityChange: Bool = false static let userProvider: ExperimentUserProvider? = nil static let analyticsProvider: ExperimentAnalyticsProvider? = nil @@ -140,6 +145,7 @@ import Foundation internal var automaticExposureTracking: Bool = ExperimentConfig.Defaults.automaticExposureTracking internal var fetchOnStart: NSNumber? = ExperimentConfig.Defaults.fetchOnStart internal var pollOnStart: Bool = true + internal var flagConfigPollingIntervalMillis = ExperimentConfig.Defaults.flagConfigPollingIntervalMillis internal var automaticFetchOnAmplitudeIdentityChange: Bool = ExperimentConfig.Defaults.automaticFetchOnAmplitudeIdentityChange internal var userProvider: ExperimentUserProvider? = ExperimentConfig.Defaults.userProvider internal var analyticsProvider: ExperimentAnalyticsProvider? = ExperimentConfig.Defaults.analyticsProvider @@ -238,6 +244,12 @@ import Foundation return self } + @discardableResult + public func flagConfigPollingIntervalMillis(_ flagConfigPollingIntervalMillis: Int) -> Builder { + self.flagConfigPollingIntervalMillis = flagConfigPollingIntervalMillis + return self + } + @discardableResult public func automaticFetchOnAmplitudeIdentityChange(_ automaticFetchOnAmplitudeIdentityChange: Bool) -> Builder { self.automaticFetchOnAmplitudeIdentityChange = automaticFetchOnAmplitudeIdentityChange @@ -290,6 +302,7 @@ import Foundation .fetchRetryOnFailure(self.retryFetchOnFailure) .automaticExposureTracking(self.automaticExposureTracking) .pollOnStart(self.pollOnStart) + .flagConfigPollingIntervalMillis(self.flagConfigPollingIntervalMillis) .automaticFetchOnAmplitudeIdentityChange(self.automaticFetchOnAmplitudeIdentityChange) .userProvider(self.userProvider) .analyticsProvider(self.analyticsProvider) @@ -317,6 +330,7 @@ import Foundation internal var automaticExposureTracking: Bool = ExperimentConfig.Defaults.automaticExposureTracking internal var fetchOnStart: NSNumber? = ExperimentConfig.Defaults.fetchOnStart internal var pollOnStart: Bool = true + internal var flagConfigPollingIntervalMillis: Int = ExperimentConfig.Defaults.flagConfigPollingIntervalMillis internal var automaticFetchOnAmplitudeIdentityChange: Bool = ExperimentConfig.Defaults.automaticFetchOnAmplitudeIdentityChange internal var userProvider: ExperimentUserProvider? = ExperimentConfig.Defaults.userProvider internal var analyticsProvider: ExperimentAnalyticsProvider? = ExperimentConfig.Defaults.analyticsProvider @@ -410,6 +424,12 @@ import Foundation return self } + @discardableResult + @objc public func flagConfigPollingIntervalMillis(_ flagConfigPollingIntervalMillis: Int) -> ExperimentConfigBuilder { + self.flagConfigPollingIntervalMillis = flagConfigPollingIntervalMillis + return self + } + @discardableResult @objc public func automaticFetchOnAmplitudeIdentityChange(_ automaticFetchOnAmplitudeIdentityChange: Bool) -> ExperimentConfigBuilder { self.automaticFetchOnAmplitudeIdentityChange = automaticFetchOnAmplitudeIdentityChange diff --git a/Tests/ExperimentTests/ExperimentClientTests.swift b/Tests/ExperimentTests/ExperimentClientTests.swift index bd8d280..c1befc9 100644 --- a/Tests/ExperimentTests/ExperimentClientTests.swift +++ b/Tests/ExperimentTests/ExperimentClientTests.swift @@ -1229,6 +1229,24 @@ class ExperimentClientTests: XCTestCase { XCTAssertEqual(retryCalled, client.startRetriesCalls) } } + + func testFlagConfigPollingIntervalConfigNotSet() { + let config = ExperimentConfigBuilder().build() + let client = DefaultExperimentClient(apiKey: "", config: config, storage: InMemoryStorage()) + XCTAssertEqual(300000, client.config.flagConfigPollingIntervalMillis) + } + + func testFlagConfigPollingIntervalConfigSetUnderMinimum() { + let config = ExperimentConfigBuilder().flagConfigPollingIntervalMillis(1000).build() + let client = DefaultExperimentClient(apiKey: "", config: config, storage: InMemoryStorage()) + XCTAssertEqual(60000, client.config.flagConfigPollingIntervalMillis) + } + + func testFlagConfigPollingIntervalConfigSetOverMinimum() { + let config = ExperimentConfigBuilder().flagConfigPollingIntervalMillis(900000).build() + let client = DefaultExperimentClient(apiKey: "", config: config, storage: InMemoryStorage()) + XCTAssertEqual(900000, client.config.flagConfigPollingIntervalMillis) + } } class TestAnalyticsProvider : ExperimentAnalyticsProvider {