Skip to content

Commit

Permalink
default skip on, persist feature flags through session (#218)
Browse files Browse the repository at this point in the history
* default to using skip

* Revert "default to using skip"

This reverts commit 2816867.

* implement default value and session feature flag consistency

* Delete contents.xcworkspacedata

---------

Co-authored-by: Mike <[email protected]>
  • Loading branch information
mike-dydx and mike-dydx committed Jul 29, 2024
1 parent 7886d7c commit 849ed21
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 34 deletions.
37 changes: 30 additions & 7 deletions StatsigInjections/StatsigInjections/StatsigInjections.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ import Combine

private let apiKey: String
private let environment: StatsigEnvironment
// ensures feature flag values stay constant throughout the app session after they are used the first time, even if they are initialized to null
private var sessionValues = [String: Availabilty]()

private enum Availabilty {
case available(Bool)
// unavailable is the case for a new feature flag, or a first launch of the app with Statsig
case unavailable
}

public enum Environment {
case production
Expand Down Expand Up @@ -67,22 +75,37 @@ import Combine
if let error {
Console.shared.log("Statsig feature flags failed to initialize: \(error)")
}
self?.newValuesSubject.send()
// intentionally not calling completion here since we do not want ff init to be blocking startup
// this may change if we need FF pre-launch
// completion()
}
}
completion()
}

private let newValuesSubject = PassthroughSubject<Void, Never>()
public var newValuesAvailablePublisher: AnyPublisher<Void, Never> {
newValuesSubject.eraseToAnyPublisher()
}

// https://docs.statsig.com/sdk/debugging
public func isOn(feature: String) -> Bool? {
Statsig.checkGate(feature)
let featureGate = Statsig.getFeatureGate(feature)
let availability: Availabilty
if let existingAvailability = sessionValues[feature] {
// a featuregate value has already been set for this session
availability = existingAvailability
} else if featureGate.evaluationDetails.reason == .Recognized {
// a featuregate will be recognized if the feature gate has been fetched in previous Statsig inits (cache is non-empty)
availability = .available(featureGate.value)
} else {
// a featuregate will be unrecognized if the feature gate is new or this is first time initializing Statsig (cache would be empty)
availability = .unavailable
}
sessionValues[feature] = availability

switch availability {
case .available(let bool):
return bool
case .unavailable:
// defer to calling context for default value
return nil
}
}

public func value(feature: String) -> String? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@ import Combine
public class CompositeFeatureFlagsProvider: NSObject & FeatureFlagsProtocol {
public var local: FeatureFlagsProtocol?
public var remote: FeatureFlagsProtocol?

public var newValuesAvailablePublisher: AnyPublisher<Void, Never> {
Publishers.Merge(local?.newValuesAvailablePublisher ?? Just(()).eraseToAnyPublisher(),
remote?.newValuesAvailablePublisher ?? Just(()).eraseToAnyPublisher())
.eraseToAnyPublisher()
}

public func refresh(completion: @escaping () -> Void) {
if let local = local {
Expand Down
9 changes: 0 additions & 9 deletions Utilities/Utilities/_Utils/_FeatureFlags/FeatureService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,6 @@ public protocol FeatureFlagsProtocol {
func isOn(feature: String) -> Bool?

func customized() -> Bool

var newValuesAvailablePublisher: AnyPublisher<Void, Never> { get }
}

// default impl
extension FeatureFlagsProtocol {
public var newValuesAvailablePublisher: AnyPublisher<Void, Never> {
return Just(()).eraseToAnyPublisher()
}
}

public class FeatureService {
Expand Down
14 changes: 12 additions & 2 deletions dydx/dydxFormatter/dydxFormatter/_Utils/dydxFeatureFlag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,28 @@ import Foundation
import Utilities

public enum dydxBoolFeatureFlag: String, CaseIterable {
case push_notification
case force_mainnet
case enable_app_rating
case shouldUseSkip = "ff_skip_migration"

var defaultValue: Bool {
switch self {
case .force_mainnet:
return false
case .enable_app_rating:
return true
case .shouldUseSkip:
return true
}
}

private static let obj = NSObject()

public var isEnabled: Bool {
if FeatureService.shared == nil {
Console.shared.log("WARNING: FeatureService not yet set up.")
}
return FeatureService.shared?.isOn(feature: rawValue) == true
return FeatureService.shared?.isOn(feature: rawValue) ?? defaultValue
}

public static var enabledFlags: [String] {
Expand Down
11 changes: 1 addition & 10 deletions dydx/dydxStateManager/dydxStateManager/AbacusStateManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ public final class AbacusStateManager: NSObject {

appConfigs.onboardingConfigs.squidVersion = OnboardingConfigs.SquidVersion.v2
appConfigs.onboardingConfigs.alchemyApiKey = CredientialConfig.shared.credential(for: "alchemyApiKey")
StatsigConfig.shared.useSkip = dydxBoolFeatureFlag.shouldUseSkip.isEnabled

return AsyncAbacusStateManagerV2(
deploymentUri: deploymentUri,
Expand All @@ -177,8 +178,6 @@ public final class AbacusStateManager: NSObject {
_ = CosmoJavascript.shared
_ = foregroundToken
_ = backgroundToken

startListeningForFeatureFlagChanges()
}

private func start() {
Expand Down Expand Up @@ -207,14 +206,6 @@ public final class AbacusStateManager: NSObject {
isStarted = true
}

private func startListeningForFeatureFlagChanges() {
FeatureService.shared?.newValuesAvailablePublisher
.sink {
StatsigConfig.shared.useSkip = dydxBoolFeatureFlag.shouldUseSkip.isEnabled
}
.store(in: &cancellables)
}

public func setMarket(market: String?) {
asyncStateManager.market = market
}
Expand Down

0 comments on commit 849ed21

Please sign in to comment.