Skip to content

Commit

Permalink
Add StatsigOptions.autoValueUpdateIntervalSec (#225)
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel-statsig authored Dec 13, 2023
1 parent f78f858 commit 25b5c58
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 28 deletions.
6 changes: 5 additions & 1 deletion Sources/Statsig/NetworkService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ class NetworkService {
self.store = store
}

func fetchUpdatedValues(for user: StatsigUser, lastSyncTimeForUser: Double, previousDerivedFields: [String: String], completion: (() -> Void)?) {
func fetchUpdatedValues(
for user: StatsigUser,
lastSyncTimeForUser: Double,
previousDerivedFields: [String: String], completion: (() -> Void)?
) {
let (body, _) = makeReqBody([
"user": user.toDictionary(forLogging: false),
"statsigMetadata": user.deviceEnvironment,
Expand Down
34 changes: 18 additions & 16 deletions Sources/Statsig/StatsigClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import Foundation
public class StatsigClient {
private static let exposureDedupeQueueLabel = "com.Statsig.exposureDedupeQueue"

internal static var autoValueUpdateTime = 10.0

internal var logger: EventLogger
internal var statsigOptions: StatsigOptions

Expand Down Expand Up @@ -639,7 +637,6 @@ extension StatsigClient {
extension StatsigClient {
private func fetchValuesFromNetwork(completion: completionBlock) {
let currentUser = self.currentUser
let shouldScheduleSync = statsigOptions.enableAutoValueUpdate
let sinceTime = self.store.getLastUpdateTime(user: currentUser)
let previousDerivedFields = self.store.getPreviousDerivedFields(user: currentUser)

Expand All @@ -652,9 +649,6 @@ extension StatsigClient {
value: nil,
metadata: ["error": errorMessage]))
}
if shouldScheduleSync {
self.scheduleRepeatingSync()
}
}

completion?(errorMessage)
Expand All @@ -664,18 +658,26 @@ extension StatsigClient {
private func scheduleRepeatingSync() {
syncTimer?.invalidate()

let currentUser = self.currentUser
syncTimer = Timer.scheduledTimer(withTimeInterval: StatsigClient.autoValueUpdateTime, repeats: false) { [weak self] _ in
guard let self = self else { return }
let timer = Timer(
timeInterval: self.statsigOptions.autoValueUpdateIntervalSec,
repeats: true,
block: { [weak self] _ in
self?.syncValuesForCurrentUser()
})
syncTimer = timer
RunLoop.current.add(timer, forMode: .common)
}

let sinceTime = self.store.getLastUpdateTime(user: currentUser)
let previousDerivedFields = self.store.getPreviousDerivedFields(user: currentUser)
private func syncValuesForCurrentUser() {
let sinceTime = self.store.getLastUpdateTime(user: currentUser)
let previousDerivedFields = self.store.getPreviousDerivedFields(user: currentUser)

self.networkService.fetchUpdatedValues(for: currentUser, lastSyncTimeForUser: sinceTime, previousDerivedFields: previousDerivedFields)
{ [weak self] in
self?.scheduleRepeatingSync()
}
}
self.networkService.fetchUpdatedValues(
for: currentUser,
lastSyncTimeForUser: sinceTime,
previousDerivedFields: previousDerivedFields,
completion: nil
)
}

private static func normalizeUser(_ user: StatsigUser?, options: StatsigOptions?) -> StatsigUser {
Expand Down
10 changes: 10 additions & 0 deletions Sources/Statsig/StatsigOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ public class StatsigOptions {
*/
public var enableAutoValueUpdate = false

/**
Only applies if StatsigOptions.enableAutoValueUpdate is true. Controls how fequently calls to refresh the current users values are made. Time is in Secibds and defaults to 60 seconds.
*/
public var autoValueUpdateIntervalSec = 60.0

/**
Overrides the auto generated StableID that is set for the device.
*/
Expand Down Expand Up @@ -67,6 +72,7 @@ public class StatsigOptions {
disableCurrentVCLogging: Bool? = false,
environment: StatsigEnvironment? = nil,
enableAutoValueUpdate: Bool? = false,
autoValueUpdateIntervalSec: Double? = nil,
overrideStableID: String? = nil,
enableCacheByFile: Bool? = false,
initializeValues: [String: Any]? = nil,
Expand Down Expand Up @@ -112,6 +118,10 @@ public class StatsigOptions {
self.shutdownOnBackground = shutdownOnBackground
}

if let internval = autoValueUpdateIntervalSec {
self.autoValueUpdateIntervalSec = internval
}

if let api = api, let url = URL(string: api) {
self.overrideURL = url
} else {
Expand Down
75 changes: 75 additions & 0 deletions Tests/StatsigTests/AutoUpdateSpec.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import Foundation

import XCTest
import Nimble
import OHHTTPStubs
import Quick
@testable import Statsig

#if !COCOAPODS
import OHHTTPStubsSwift
#endif

final class AutoUpdateSpec: BaseSpec {
override func spec() {
super.spec()

let opts = StatsigOptions(
enableAutoValueUpdate: true,
autoValueUpdateIntervalSec: 0.001,
api: "http://AutoUpdateSpec"
)

it("syncs when started from the background thread") {
Statsig.shutdown()

var callsMade = 0
stub(condition: isHost("AutoUpdateSpec")) { req in
if (req.url?.absoluteString.contains("/initialize") ?? false) {
callsMade += 1
}
return HTTPStubsResponse(jsonObject: [:], statusCode: 200, headers: nil)
}

DispatchQueue.global().sync {
TestUtils.startStatsigAndWait(key: "client-key", nil, opts)
}

expect(callsMade).toEventually(equal(10))
}

it("pulls in changed values") {
Statsig.shutdown()

var response = [
"feature_gates": [:],
"dynamic_configs": [:],
"layer_configs": [:],
"has_updates": true
]

stub(condition: isHost("AutoUpdateSpec")) { req in
return HTTPStubsResponse(jsonObject: response, statusCode: 200, headers: nil)
}

TestUtils.startStatsigAndWait(key: "client-key", nil, opts)

expect(Statsig.checkGate("a_gate")).to(beFalse())

response = [
"feature_gates": [
"a_gate".sha256(): [
"value": true,
"rule_id": "rule_id_1",
"secondary_exposures": []
]
],
"dynamic_configs": [:],
"layer_configs": [:],
"has_updates": true
]

expect(Statsig.checkGate("a_gate")).toEventually(beTrue())
}
}
}
4 changes: 0 additions & 4 deletions Tests/StatsigTests/BaseSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ class BaseSpec: QuickSpec {
fatalError("Statsig.client not cleared")
}

if (StatsigClient.autoValueUpdateTime != 10.0) {
fatalError("autoValueUpdate not reset")
}

waitUntil { done in
while (Statsig.client != nil) {}
done()
Expand Down
1 change: 0 additions & 1 deletion Tests/StatsigTests/NullInitialize.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ class NullInitializeSpec: BaseSpec {
describe("Nil and Statsig") {
beforeEach {
TestUtils.clearStorage()
StatsigClient.autoValueUpdateTime = 10

stub(condition: isHost("api.statsig.com")) { _ in
HTTPStubsResponse(jsonObject: StatsigSpec.mockUserValues, statusCode: 200, headers: nil)
Expand Down
1 change: 0 additions & 1 deletion Tests/StatsigTests/SDKKeySpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ class SDKKeySpec: BaseSpec {
describe("SDK Keys") {
beforeEach {
TestUtils.clearStorage()
StatsigClient.autoValueUpdateTime = 10
}

afterEach {
Expand Down
15 changes: 10 additions & 5 deletions Tests/StatsigTests/StatsigSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ class StatsigSpec: BaseSpec {
describe("starting Statsig") {
beforeEach {
TestUtils.clearStorage()
StatsigClient.autoValueUpdateTime = 10
}

afterEach {
Expand Down Expand Up @@ -80,7 +79,6 @@ class StatsigSpec: BaseSpec {
var requestCount = 0
var lastSyncTime: Double = 0
let now = NSDate().timeIntervalSince1970
StatsigClient.autoValueUpdateTime = 0.1

stub(condition: isHost("api.statsig.enableAutoValueUpdateTest")) { request in
requestCount += 1
Expand All @@ -93,7 +91,11 @@ class StatsigSpec: BaseSpec {
return HTTPStubsResponse(jsonObject: ["time": now * 1000], statusCode: 200, headers: nil)
}

let opts = StatsigOptions(disableDiagnostics: true)
let opts = StatsigOptions(
enableAutoValueUpdate: false,
autoValueUpdateIntervalSec: 0.1,
disableDiagnostics: true
)
opts.overrideURL = URL(string: "http://api.statsig.enableAutoValueUpdateTest")
Statsig.start(sdkKey: "client-api-key", options: opts)

Expand All @@ -106,9 +108,12 @@ class StatsigSpec: BaseSpec {
var requestCount = 0
var lastSyncTime: Double = 0
let now = NSDate().timeIntervalSince1970
StatsigClient.autoValueUpdateTime = 0.1

let opts = StatsigOptions(enableAutoValueUpdate: true, disableDiagnostics: true)
let opts = StatsigOptions(
enableAutoValueUpdate: true,
autoValueUpdateIntervalSec: 0.1,
disableDiagnostics: true
)
opts.overrideURL = URL(string: "http://StatsigSpec.enableAutoValueUpdateEQtrue")

var requestExpectation = self.expectation(description: "Request Made Once")
Expand Down

0 comments on commit 25b5c58

Please sign in to comment.