diff --git a/ConfidenceDemoApp/ConfidenceDemoApp/ConfidenceDemoApp.swift b/ConfidenceDemoApp/ConfidenceDemoApp/ConfidenceDemoApp.swift index 3b3eb586..3bb4737f 100644 --- a/ConfidenceDemoApp/ConfidenceDemoApp/ConfidenceDemoApp.swift +++ b/ConfidenceDemoApp/ConfidenceDemoApp/ConfidenceDemoApp.swift @@ -19,10 +19,18 @@ extension ConfidenceDemoApp { guard let secret = ProcessInfo.processInfo.environment["CLIENT_SECRET"] else { return } + + // If we have no cache, then do a fetch first. + var initializationStratgey: InitializationStrategy = .activateAndFetchAsync + if ConfidenceFeatureProvider.isStorageEmpty() { + initializationStratgey = .fetchAndActivate + } + let provider = ConfidenceFeatureProvider .Builder(credentials: .clientSecret(secret: secret)) - .with(initializationStrategy: .activateAndFetchAsync) + .with(initializationStrategy: initializationStratgey) .build() + // NOTE: Using a random UUID for each app start is not advised and can result in getting stale values. let ctx = MutableContext(targetingKey: UUID.init().uuidString, structure: MutableStructure()) OpenFeatureAPI.shared.setProvider(provider: provider, initialContext: ctx) } diff --git a/ConfidenceDemoApp/ConfidenceDemoApp/ContentView.swift b/ConfidenceDemoApp/ConfidenceDemoApp/ContentView.swift index e4c263df..bced791a 100644 --- a/ConfidenceDemoApp/ConfidenceDemoApp/ContentView.swift +++ b/ConfidenceDemoApp/ConfidenceDemoApp/ContentView.swift @@ -18,7 +18,7 @@ struct ContentView: View { text.text = OpenFeatureAPI .shared .getClient() - .getStringValue(key: "hawkflag.color", defaultValue: "ERROR") + .getStringValue(key: "swift-demoapp.color", defaultValue: "ERROR") if text.text == "Green" { color.color = .green } else if text.text == "Yellow" { diff --git a/README.md b/README.md index 4eda7125..ffb198a6 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,15 @@ The evaluation context is the way for the client to specify contextual data that The `setProvider()` function is synchronous and returns immediately, however this does not mean that the provider is ready to be used. An asynchronous network request to the Confidence backend to fetch all the flags configured for your application must be completed by the provider first. The provider will then emit a _READY_ event indicating you can start resolving flags. +A ultity function is available on the provider to check if the current storage has any values stored - this can be used to determine the best initialization strategy. +```swift +// If we have no cache, then do a fetch first. +var initializationStratgey: InitializationStrategy = .activateAndFetchAsync +if ConfidenceFeatureProvider.isStorageEmpty() { + initializationStratgey = .fetchAndActivate +} +``` + To listen for the _READY_ event, you can add an event handler via the `OpenFeatureAPI` shared instance: ```swift func providerReady(notification: Notification) { diff --git a/Sources/ConfidenceProvider/Cache/DefaultStorage.swift b/Sources/ConfidenceProvider/Cache/DefaultStorage.swift index 25ed5f0c..d4942f73 100644 --- a/Sources/ConfidenceProvider/Cache/DefaultStorage.swift +++ b/Sources/ConfidenceProvider/Cache/DefaultStorage.swift @@ -56,7 +56,7 @@ public class DefaultStorage: Storage { public func isEmpty() -> Bool { guard let data = try? read() else { - return false + return true } return data.isEmpty @@ -97,3 +97,17 @@ public class DefaultStorage: Storage { components: resolverCacheBundleId, "\(bundleIdentifier)", filePath) } } + +extension DefaultStorage { + public static func resolverFlagsCache() -> DefaultStorage { + DefaultStorage(filePath: "resolver.flags.cache") + } + + public static func resolverApplyCache() -> DefaultStorage { + DefaultStorage(filePath: "resolver.apply.cache") + } + + public static func applierFlagsCache() -> DefaultStorage { + DefaultStorage(filePath: "applier.flags.cache") + } +} diff --git a/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift b/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift index 39c183dd..43c3f313 100644 --- a/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift +++ b/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift @@ -89,10 +89,6 @@ public class ConfidenceFeatureProvider: FeatureProvider { } } - public func isStorageEmpty() -> Bool { - storage.isEmpty() - } - public func onContextSet( oldContext: OpenFeature.EvaluationContext?, newContext: OpenFeature.EvaluationContext @@ -382,16 +378,28 @@ public class ConfidenceFeatureProvider: FeatureProvider { } } +// MARK: Storage + +extension ConfidenceFeatureProvider { + public static func isStorageEmpty( + storage: Storage = DefaultStorage.resolverFlagsCache() + ) -> Bool { + storage.isEmpty() + } +} + +// MARK: Builder + extension ConfidenceFeatureProvider { public struct Builder { var options: ConfidenceClientOptions var session: URLSession? var localOverrides: [String: LocalOverride] = [:] - var storage: Storage = DefaultStorage(filePath: "resolver.flags.cache") + var storage: Storage = DefaultStorage.resolverFlagsCache() var cache: ProviderCache? var flagApplier: (any FlagApplier)? var initializationStrategy: InitializationStrategy = .fetchAndActivate - var applyStorage: Storage = DefaultStorage(filePath: "resolver.apply.cache") + var applyStorage: Storage = DefaultStorage.resolverApplyCache() /// Initializes the builder with the given credentails. /// @@ -565,7 +573,7 @@ extension ConfidenceFeatureProvider { flagApplier ?? FlagApplierWithRetries( httpClient: NetworkClient(region: options.region), - storage: DefaultStorage(filePath: "applier.flags.cache"), + storage: DefaultStorage.applierFlagsCache(), options: options )