### Install and Run SwiftLint Source: https://github.com/open-feature/swift-sdk/blob/main/CONTRIBUTING.md Install SwiftLint using Homebrew and run it to lint the code. This ensures code quality and adherence to Swift style guidelines. ```shell brew install swiftlint swiftlint ``` -------------------------------- ### iOS Usage Example Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Example of how to initialize the OpenFeature SDK, set a provider, and retrieve a boolean flag value in Swift. Ensure the provider is configured and has completed its initialization tasks before fetching flag values. ```swift import OpenFeature Task { let provider = CustomProvider() // configure a provider, wait for it to complete its initialization tasks await OpenFeatureAPI.shared.setProviderAndWait(provider: provider) // get a bool flag value let client = OpenFeatureAPI.shared.getClient() let flagValue = client.getBooleanValue(key: "boolFlag", defaultValue: false) } ``` -------------------------------- ### Install CocoaPods Source: https://github.com/open-feature/swift-sdk/blob/main/OWNERS.md Use this command to install the CocoaPods package manager if it is not already present on your system. ```bash gem install cocoapods ``` -------------------------------- ### Basic MultiProvider Setup in OpenFeature Swift SDK Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Combine multiple feature flag providers into a single MultiProvider for flexible flag evaluation. This setup requires creating individual providers, instantiating MultiProvider, and setting it as the global provider. ```swift import OpenFeature Task { // Create individual providers let primaryProvider = PrimaryProvider() let fallbackProvider = FallbackProvider() // Create a MultiProvider with default FirstMatchStrategy let multiProvider = MultiProvider(providers: [primaryProvider, fallbackProvider]) // Set the MultiProvider as the global provider await OpenFeatureAPI.shared.setProviderAndWait(provider: multiProvider) // Use flags normally - the MultiProvider will handle provider selection let client = OpenFeatureAPI.shared.getClient() let flagValue = client.getBooleanValue(key: "my-flag", defaultValue: false) } ``` -------------------------------- ### Install OpenFeature SDK with CocoaPods Source: https://context7.com/open-feature/swift-sdk/llms.txt Integrate the OpenFeature SDK into your project using CocoaPods by adding the specified pod to your Podfile. ```ruby # Podfile pod 'OpenFeature', '~> 0.5.0' ``` -------------------------------- ### Install OpenFeature SDK with Swift Package Manager Source: https://context7.com/open-feature/swift-sdk/llms.txt Add the OpenFeature SDK as a dependency in your Swift Package Manager configuration. ```swift // Package.swift dependencies section .package(url: "git@github.com:open-feature/swift-sdk.git", from: "0.5.0") // Target dependencies section .product(name: "OpenFeature", package: "swift-sdk") ``` -------------------------------- ### Get OpenFeature Client Instance Source: https://context7.com/open-feature/swift-sdk/llms.txt Obtain a client instance to interact with the OpenFeature API. Clients can be anonymous or named, with the latter including version information for better context in hooks and metadata. ```swift Task { await OpenFeatureAPI.shared.setProviderAndWait(provider: MyProvider()) // Default anonymous client let client = OpenFeatureAPI.shared.getClient() // Named client (name appears in hook context and metadata) let namedClient = OpenFeatureAPI.shared.getClient(name: "checkout-service", version: "2.1.0") let isEnabled = client.getBooleanValue(key: "dark-mode", defaultValue: false) print("dark-mode:", isEnabled) // true (per MyProvider stub above) } ``` -------------------------------- ### Combine Providers with `MultiProvider` Source: https://context7.com/open-feature/swift-sdk/llms.txt Combines multiple `FeatureProvider` instances using either `FirstMatchStrategy` (returns the first non-'flag not found' result) or `FirstSuccessfulStrategy` (returns the first error-free result). Useful for provider migration or high-availability setups. Ensure the provider is set and awaited. ```swift import OpenFeature Task { // Provider migration: check new provider first, fall back to legacy let multiProvider = MultiProvider( providers: [ NewRemoteProvider(), LegacyProvider() ], strategy: FirstMatchStrategy() // default ) await OpenFeatureAPI.shared.setProviderAndWait(provider: multiProvider) let client = OpenFeatureAPI.shared.getClient() // Evaluated against NewRemoteProvider first; falls through to LegacyProvider // only when the flag is not found in the new provider let value = client.getBooleanValue(key: "new-feature", defaultValue: false) print("new-feature:", value) // High-availability: silently skip any failing provider let haProvider = MultiProvider( providers: [ PrimaryRemoteProvider(), LocalCacheProvider(), StaticDefaultsProvider() ], strategy: FirstSuccessfulStrategy() ) await OpenFeatureAPI.shared.setProviderAndWait(provider: haProvider) } ``` -------------------------------- ### Implement a Custom Hook in Swift Source: https://github.com/open-feature/swift-sdk/blob/main/README.md To create a custom hook, implement the `Hook` interface and define all required methods: `before`, `after`, `error`, and `finally`. This example shows a basic structure for a boolean hook. ```swift class BooleanHook: Hook { typealias HookValue = Bool func before(ctx: HookContext, hints: [String: Any]) { // do something } func after(ctx: HookContext, details: FlagEvaluationDetails, hints: [String: Any]) { // do something } func error(ctx: HookContext, error: Error, hints: [String: Any]) { // do something } func finally(ctx: HookContext, details: FlagEvaluationDetails, hints: [String: Any]) { // do something } } ``` -------------------------------- ### Configure Environment-Specific Providers Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Set up different providers for different environments using a combination of EnvironmentProvider and a default provider. ```swift // Different providers for different environments let providers = [ EnvironmentProvider(environment: "production"), DefaultProvider() ] let multiProvider = MultiProvider(providers: providers) ``` -------------------------------- ### `OpenFeatureAPI.shared.setProvider(provider:)` Source: https://context7.com/open-feature/swift-sdk/llms.txt Fire-and-forget variant of provider registration. Returns immediately; readiness must be determined by polling `getProviderStatus()` or subscribing via `observe()`. ```APIDOC ## `OpenFeatureAPI.shared.setProvider(provider:)` ### Description Fire-and-forget variant of provider registration. Returns immediately; readiness must be determined by polling `getProviderStatus()` or subscribing via `observe()`. ### Method ```swift OpenFeatureAPI.shared.setProvider(provider: MyProvider()) ``` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```swift Task { // Non-blocking — initialization runs in background OpenFeatureAPI.shared.setProvider(provider: MyProvider()) // Poll or subscribe to know when ready let status = OpenFeatureAPI.shared.getProviderStatus() // may be .notReady immediately after print("Current status:", status) } ``` ### Response #### Success Response (200) None explicitly defined, operation is synchronous. #### Response Example None ``` -------------------------------- ### Build and Use ImmutableContext in Swift Source: https://context7.com/open-feature/swift-sdk/llms.txt Demonstrates how to create an immutable context with targeting keys and attributes, perform functional modifications, set it globally or wait for it, and read it back. ```swift import OpenFeature // Build a context from scratch let ctx = ImmutableContext( targetingKey: "user-42", structure: ImmutableStructure(attributes: [ "email": .string("alice@example.com"), "plan": .string("pro"), "beta": .boolean(true), "loginCount": .integer(17), "score": .double(98.6), "country": .string("US") ]) ) // Functional builder-style modifications (returns new instances) let enriched = ctx .withAttribute(key: "device", value: .string("iPhone")) .withAttributes(["os": .string("iOS 18"), "region": .string("west")]) // Set globally — all subsequent evaluations use this context OpenFeatureAPI.shared.setEvaluationContext(evaluationContext: enriched) // Or wait for the provider's onContextSet to complete await OpenFeatureAPI.shared.setEvaluationContextAndWait(evaluationContext: enriched) // Read back if let stored = OpenFeatureAPI.shared.getEvaluationContext() { print("Targeting key:", stored.getTargetingKey()) // "user-42" print("Plan:", stored.getValue(key: "plan") as Any) // Value.string("pro") } ``` -------------------------------- ### Integrating swift-log with OpenFeature SDK Source: https://context7.com/open-feature/swift-sdk/llms.txt Demonstrates how to configure logging at global, client, and per-evaluation levels. The most specific logger configured will take precedence. Loggers can also be removed. ```swift import Logging import OpenFeature // 1. Global logger — affects all evaluations unless overridden var globalLogger = Logger(label: "com.app.openfeature") globalLogger.logLevel = .debug OpenFeatureAPI.shared.setLogger(globalLogger) // 2. Client-level logger — overrides global for this client let client = OpenFeatureAPI.shared.getClient(name: "checkout", version: "3.0") var clientLogger = Logger(label: "com.app.checkout.flags") clientLogger.logLevel = .warning client.setLogger(clientLogger) // 3. Evaluation-level logger — overrides client logger for one call var evalLogger = Logger(label: "com.app.payment.critical") evalLogger.logLevel = .trace let value = client.getBooleanValue( key: "new-payment-flow", defaultValue: false, options: FlagEvaluationOptions(logger: evalLogger) ) // Remove loggers OpenFeatureAPI.shared.setLogger(nil) // disables SDK-level logging ``` -------------------------------- ### Run Tests from Command Line Source: https://github.com/open-feature/swift-sdk/blob/main/CONTRIBUTING.md Execute all project tests directly from the command line. This is useful for verifying code changes before committing. ```shell swift test ``` -------------------------------- ### Format Code with Swift Format Source: https://github.com/open-feature/swift-sdk/blob/main/CONTRIBUTING.md Automatically format your code using the provided script. This ensures consistent code style across the project. ```shell ./scripts/swift-format ``` -------------------------------- ### Constructing and Accessing OpenFeature Values Source: https://context7.com/open-feature/swift-sdk/llms.txt Demonstrates how to create different types of `Value` enums (boolean, string, integer, double, date, list, structure) and how to safely access their underlying data using type-specific accessors. Also shows generic factory method and decoding into a Decodable model. ```swift import OpenFeature // Constructing Values let boolVal = Value.boolean(true) let strVal = Value.string("blue") let intVal = Value.integer(42) let dblVal = Value.double(3.14) let dateVal = Value.date(Date()) let listVal = Value.list([.string("a"), .string("b"), .integer(1)]) let structVal = Value.structure([ "enabled": .boolean(true), "limit": .integer(100), "tags": .list([.string("beta"), .string("internal")]) ]) // Type-safe accessors print(boolVal.asBoolean() ?? false) // true print(strVal.asString() ?? "") // "blue" print(structVal.asStructure()?["limit"]?.asInteger() ?? 0) // 100 // Generic factory let inferred = Value.of(Int64(7)) // Value.integer(7) // Decoding into a Decodable model struct Config: Decodable { let enabled: Bool; let limit: Int } let config: Config? = try? structVal.decode() print(config?.enabled, config?.limit as Any) // Optional(true) Optional(100) // Null check print(Value.null.isNull()) // true ``` -------------------------------- ### Migrate Providers Gradually with MultiProvider Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Use MultiProvider to gradually migrate from an old provider to a new one by checking the new provider first and falling back to the old one if necessary. ```swift // Gradually migrate from OldProvider to NewProvider let multiProvider = MultiProvider(providers: [ NewProvider(), // Check new provider first OldProvider() // Fall back to old provider ]) ``` -------------------------------- ### Register for CocoaPods Trunk Token Source: https://github.com/open-feature/swift-sdk/blob/main/OWNERS.md Run this command to register with CocoaPods trunk and generate a new verification email. Ensure you use the correct email address for the OpenFeature core team. ```bash pod trunk register openfeature-core@groups.io 'OpenFeature' --description='OpenFeature Deployment User' ``` -------------------------------- ### MultiProvider with FirstSuccessfulStrategy in OpenFeature Swift SDK Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Implement a MultiProvider using the FirstSuccessfulStrategy. This strategy continues evaluating providers even if an error occurs, returning the first provider that completes without any error. ```swift let multiProvider = MultiProvider( providers: [primaryProvider, fallbackProvider], strategy: FirstSuccessfulStrategy() ) ``` -------------------------------- ### Register Provider and Wait for Initialization Source: https://context7.com/open-feature/swift-sdk/llms.txt Registers a FeatureProvider with the global OpenFeatureAPI singleton and waits for its initialization to complete. Use this when the provider must be ready before evaluating flags. ```swift import OpenFeature // Minimal provider stub used for illustration final class MyProvider: FeatureProvider { var hooks: [any Hook] = [] var metadata: ProviderMetadata = MyMetadata() private let statusTracker = ProviderStatusTracker() var status: ProviderStatus { statusTracker.status } func observe() -> AnyPublisher { statusTracker.observe() } func initialize(initialContext: EvaluationContext?) -> Future { Future { promise in // Fetch remote flag config here... self.statusTracker.send(.ready(nil)) promise(.success(())) } } func onContextSet(oldContext: EvaluationContext?, newContext: EvaluationContext) -> Future { Future { promise in promise(.success(())) } } func getBooleanEvaluation(key: String, defaultValue: Bool, context: EvaluationContext?) throws -> ProviderEvaluation { ProviderEvaluation(value: key == "dark-mode" ? true : defaultValue) } func getStringEvaluation(key: String, defaultValue: String, context: EvaluationContext?) throws -> ProviderEvaluation { ProviderEvaluation(value: defaultValue) } func getIntegerEvaluation(key: String, defaultValue: Int64, context: EvaluationContext?) throws -> ProviderEvaluation { ProviderEvaluation(value: defaultValue) } func getDoubleEvaluation(key: String, defaultValue: Double, context: EvaluationContext?) throws -> ProviderEvaluation { ProviderEvaluation(value: defaultValue) } func getObjectEvaluation(key: String, defaultValue: Value, context: EvaluationContext?) throws -> ProviderEvaluation { ProviderEvaluation(value: defaultValue) } } struct MyMetadata: ProviderMetadata { var name: String? = "MyProvider" } // Usage — typically called once at app launch (e.g., in @main App.init or AppDelegate) Task { await OpenFeatureAPI.shared.setProviderAndWait(provider: MyProvider()) // Provider is fully initialized here; safe to evaluate flags print("Provider ready:", OpenFeatureAPI.shared.getProviderStatus()) // ProviderStatus.ready } ``` -------------------------------- ### Configuring Per-Evaluation Flag Evaluation Options Source: https://context7.com/open-feature/swift-sdk/llms.txt Shows how to bundle custom hooks, hook hints, and a dedicated logger for a single flag evaluation using `FlagEvaluationOptions`. This allows for fine-grained control over individual flag reads. ```swift import Logging import OpenFeature let perCallLogger = Logger(label: "com.app.critical-path") let options = FlagEvaluationOptions( hooks: [AuditHook(), MetricsHook()], hookHints: [ "request-id": "req-abc-123", "user-tier": "enterprise" ], logger: perCallLogger ) let details = client.getBooleanDetails( key: "maintenance-mode", defaultValue: false, options: options ) if let errorCode = details.errorCode { // Handle resolution errors switch errorCode { case .flagNotFound: print("Flag not configured — using default:", details.value) case .providerNotReady: print("Provider not yet initialized") case .typeMismatch: print("Flag type does not match requested type") default: print("Evaluation error:", details.errorMessage ?? "unknown") } } else { print("Resolved:", details.value, "via:", details.reason ?? "unknown") } ``` -------------------------------- ### Implement High Availability with Multiple Providers Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Configure MultiProvider with multiple providers to ensure high availability and redundancy for flag evaluations. ```swift // Use multiple providers for redundancy let multiProvider = MultiProvider(providers: [ RemoteProvider(), LocalCacheProvider(), StaticProvider() ]) ``` -------------------------------- ### Track User Events with `client.track` Source: https://context7.com/open-feature/swift-sdk/llms.txt Associates named user actions with optional numeric values and structured attributes. Use this to link conversion events back to feature flag evaluations for experimentation analysis. Ensure the provider is set and awaited before tracking. ```swift Task { await OpenFeatureAPI.shared.setProviderAndWait(provider: MyProvider()) let client = OpenFeatureAPI.shared.getClient() // Simple event with no data client.track(key: "button-clicked") // Event with a numeric value (e.g., purchase amount) client.track( key: "purchase-completed", details: ImmutableTrackingEventDetails(value: 49.99) ) // Event with structured attributes and a numeric value let details = ImmutableTrackingEventDetails(value: 99.0) .withAttribute(key: "sku", value: .string("PRD-007")) .withAttribute(key: "currency", value: .string("USD")) .withAttributes(["channel": .string("mobile"), "experiment": .string("checkout-v2")]) client.track( key: "add-to-cart", context: ImmutableContext(targetingKey: "user-42"), details: details ) } ``` -------------------------------- ### Observe Provider Events with Combine Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Subscribe to provider events using Combine's `observe()` method to react to state changes like provider readiness or errors. ```swift let cancellable = OpenFeatureAPI.shared.observe().sink { switch event { case ProviderEvent.ready: // ... default: // ... } } ``` -------------------------------- ### `OpenFeatureAPI.shared.setProviderAndWait(provider:)` Source: https://context7.com/open-feature/swift-sdk/llms.txt Registers a `FeatureProvider` with the global singleton and awaits the completion of its `initialize` lifecycle method before returning. Use this when you need the provider to be fully ready before evaluating any flags. ```APIDOC ## `OpenFeatureAPI.shared.setProviderAndWait(provider:)` ### Description Registers a `FeatureProvider` with the global singleton and awaits the completion of its `initialize` lifecycle method before returning. Use this when you need the provider to be fully ready before evaluating any flags. ### Method ```swift await OpenFeatureAPI.shared.setProviderAndWait(provider: MyProvider()) ``` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```swift // Minimal provider stub used for illustration final class MyProvider: FeatureProvider { var hooks: [any Hook] = [] var metadata: ProviderMetadata = MyMetadata() private let statusTracker = ProviderStatusTracker() var status: ProviderStatus { statusTracker.status } func observe() -> AnyPublisher { statusTracker.observe() } func initialize(initialContext: EvaluationContext?) -> Future { Future { promise in // Fetch remote flag config here... self.statusTracker.send(.ready(nil)) promise(.success(())) } } func onContextSet(oldContext: EvaluationContext?, newContext: EvaluationContext) -> Future { Future { promise in promise(.success(())) } } func getBooleanEvaluation(key: String, defaultValue: Bool, context: EvaluationContext?) throws -> ProviderEvaluation { ProviderEvaluation(value: key == "dark-mode" ? true : defaultValue) } func getStringEvaluation(key: String, defaultValue: String, context: EvaluationContext?) throws -> ProviderEvaluation { ProviderEvaluation(value: defaultValue) } func getIntegerEvaluation(key: String, defaultValue: Int64, context: EvaluationContext?) throws -> ProviderEvaluation { ProviderEvaluation(value: defaultValue) } func getDoubleEvaluation(key: String, defaultValue: Double, context: EvaluationContext?) throws -> ProviderEvaluation { ProviderEvaluation(value: defaultValue) } func getObjectEvaluation(key: String, defaultValue: Value, context: EvaluationContext?) throws -> ProviderEvaluation { ProviderEvaluation(value: defaultValue) } } struct MyMetadata: ProviderMetadata { var name: String? = "MyProvider" } // Usage — typically called once at app launch (e.g., in @main App.init or AppDelegate) Task { await OpenFeatureAPI.shared.setProviderAndWait(provider: MyProvider()) // Provider is fully initialized here; safe to evaluate flags print("Provider ready:", OpenFeatureAPI.shared.getProviderStatus()) // ProviderStatus.ready } ``` ### Response #### Success Response (200) None explicitly defined, operation is synchronous after initialization. #### Response Example None ``` -------------------------------- ### Implement a Custom OpenFeature Provider Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Develop a custom feature provider by implementing the `FeatureProvider` interface. Use `ProviderStatusTracker` to manage status and thread safety. ```swift import Combine import OpenFeature final class CustomProvider: FeatureProvider { var hooks: [any Hook] = [] var metadata: ProviderMetadata = CustomMetadata() // ProviderStatusTracker keeps `status` in sync with emitted events, // handles thread safety, and replays the current status to new subscribers. private let statusTracker = ProviderStatusTracker() var status: ProviderStatus { statusTracker.status } func observe() -> AnyPublisher { statusTracker.observe() } func initialize(initialContext: EvaluationContext?) -> Future { Future { promise in // Perform context-aware initialisation, then emit any non-.notReady status. // .ready and .error are the most common outcomes. self.statusTracker.send(.ready(nil)) promise(.success(())) } } func onContextSet(oldContext: EvaluationContext?, newContext: EvaluationContext) -> Future { // Note: this may be called again before a previous lifecycle Future has // resolved. Cancel any in-flight async work when a new call arrives. Future { promise in self.statusTracker.send(.reconciling(nil)) // ... re-initialize with new context ... self.statusTracker.send(.contextChanged(nil)) // or .error(nil) on failure promise(.success(())) } } func getBooleanEvaluation( key: String, defaultValue: Bool, context: EvaluationContext? ) throws -> ProviderEvaluation { // resolve a boolean flag value } ... } ``` -------------------------------- ### Track Events with OpenFeature Swift SDK Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Use the tracking API to associate user actions with feature flag evaluations for experimentation. This requires obtaining a client and calling the track function with an event key and optional details. ```swift let client = OpenFeatureAPI.shared.getClient() // Track an event client.track(key: "test") // Track an event with a numeric value client.track(key: "test-value", details: ImmutableTrackingEventDetails(value: 5)) ``` -------------------------------- ### Configure Global Logger for OpenFeature Swift SDK Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Set a global logger for the OpenFeature SDK to affect all flag evaluations. Ensure the 'Logging' framework is imported. ```swift import Logging let logger = Logger(label: "com.example.app.openfeature") OpenFeatureAPI.shared.setLogger(logger) ``` -------------------------------- ### Add CocoaPods Dependency Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Add the OpenFeature SDK to your Podfile for integration using CocoaPods. ```ruby pod 'OpenFeature', '~> 0.5.0' ``` -------------------------------- ### Add Swift Package Manager Dependency Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Add the OpenFeature SDK as a dependency in your Package.swift file for Swift Package Manager integration. ```swift .package(url: "git@github.com:open-feature/swift-sdk.git", from: "0.5.0") ``` ```swift .product(name: "OpenFeature", package: "swift-sdk"), ``` -------------------------------- ### MultiProvider with FirstMatchStrategy in OpenFeature Swift SDK Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Configure a MultiProvider to use the FirstMatchStrategy, which returns the first successful flag evaluation result from the ordered list of providers. Errors other than 'flag not found' will halt evaluation. ```swift let multiProvider = MultiProvider( providers: [primaryProvider, fallbackProvider], strategy: FirstMatchStrategy() ) ``` -------------------------------- ### Implement AuditHook for Flag Evaluation Lifecycle in Swift Source: https://context7.com/open-feature/swift-sdk/llms.txt Shows how to create a custom Hook to log flag evaluation events at different stages (before, after, error, finally). Hooks can be added globally, per-client, or per-evaluation. ```swift import OpenFeature // Logging hook that records evaluation details class AuditHook: Hook { typealias HookValue = Bool // restrict to boolean flags; use String, Int64, Double, Value, or AnyHashable func before(ctx: HookContext, hints: [String: Any]) { print("[AUDIT] Evaluating flag:", ctx.flagKey, "type:", ctx.type) } func after( ctx: HookContext, details: FlagEvaluationDetails, hints: [String: Any] ) { print("[AUDIT] Resolved:", ctx.flagKey, "→", details.value, "variant:", details.variant ?? "-", "reason:", details.reason ?? "-") } func error(ctx: HookContext, error: Error, hints: [String: Any]) { print("[AUDIT] Error on flag:", ctx.flagKey, error) } func finally( ctx: HookContext, details: FlagEvaluationDetails, hints: [String: Any] ) { print("[AUDIT] Evaluation complete:", ctx.flagKey) } } Task { await OpenFeatureAPI.shared.setProviderAndWait(provider: MyProvider()) // Global hook — runs on every evaluation across all clients OpenFeatureAPI.shared.addHooks(hooks: AuditHook()) let client = OpenFeatureAPI.shared.getClient() // Client-level hook — runs on all evaluations from this client client.addHooks(AuditHook()) // Per-evaluation hook — runs only for this call let options = FlagEvaluationOptions(hooks: [AuditHook()]) let value = client.getBooleanValue(key: "dark-mode", defaultValue: false, options: options) print("Value:", value) // [AUDIT] Evaluating flag: dark-mode type: boolean // [AUDIT] Resolved: dark-mode → true variant: - reason: - // [AUDIT] Evaluation complete: dark-mode } ``` -------------------------------- ### Observe Provider Events using Combine in Swift Source: https://context7.com/open-feature/swift-sdk/llms.txt Subscribes to lifecycle events from the active OpenFeature provider using Combine's AnyPublisher. Useful for reacting to provider readiness, errors, and configuration changes. ```swift import Combine import OpenFeature var cancellables = Set() // Subscribe before setting the provider to capture .ready on first initialization OpenFeatureAPI.shared.observe() .sink { event in switch event { case .ready(let details): print("Provider ready:", details as Any) // Safe to evaluate flags now case .error(let details): print("Provider error — code:", details?.errorCode as Any) // Fall back to defaults or retry case .configurationChanged(let details): print("Flags changed:", details as Any) // Re-evaluate cached flag values case .stale(let details): print("Data stale:", details as Any) case .reconciling(let details): print("Context reconciling:", details as Any) case .contextChanged(let details): print("Context changed:", details as Any) } } .store(in: &cancellables) Task { await OpenFeatureAPI.shared.setProviderAndWait(provider: MyProvider()) // .ready event fires into the subscriber above } ``` -------------------------------- ### MultiProvider - Combining Providers Source: https://context7.com/open-feature/swift-sdk/llms.txt Combines multiple `FeatureProvider` instances behind a single interface. Supports two evaluation strategies: `FirstMatchStrategy` and `FirstSuccessfulStrategy`. ```APIDOC ## MultiProvider ### Description Combines multiple `FeatureProvider` instances behind a single interface. Supports two evaluation strategies: `FirstMatchStrategy` (returns first non-"flag not found" result, propagates other errors) and `FirstSuccessfulStrategy` (returns first error-free result, silently skips any error). ### Method Swift SDK class ### Parameters - **providers** (Array) - Required - An array of `FeatureProvider` instances to combine. - **strategy** (EvaluationStrategy) - Optional - The strategy to use for evaluating flags across providers. Defaults to `FirstMatchStrategy`. ### Usage Examples ```swift import OpenFeature Task { // Provider migration: check new provider first, fall back to legacy let multiProvider = MultiProvider( providers: [ NewRemoteProvider(), LegacyProvider() ], strategy: FirstMatchStrategy() // default ) await OpenFeatureAPI.shared.setProviderAndWait(provider: multiProvider) let client = OpenFeatureAPI.shared.getClient() // Evaluated against NewRemoteProvider first; falls through to LegacyProvider // only when the flag is not found in the new provider let value = client.getBooleanValue(key: "new-feature", defaultValue: false) print("new-feature:", value) // High-availability: silently skip any failing provider let haProvider = MultiProvider( providers: [ PrimaryRemoteProvider(), LocalCacheProvider(), StaticDefaultsProvider() ], strategy: FirstSuccessfulStrategy() ) await OpenFeatureAPI.shared.setProviderAndWait(provider: haProvider) } ``` ``` -------------------------------- ### Register Provider without Waiting for Initialization Source: https://context7.com/open-feature/swift-sdk/llms.txt Registers a FeatureProvider with the global OpenFeatureAPI singleton in a fire-and-forget manner. Initialization occurs in the background, and readiness must be checked separately. ```swift Task { // Non-blocking — initialization runs in background OpenFeatureAPI.shared.setProvider(provider: MyProvider()) // Poll or subscribe to know when ready let status = OpenFeatureAPI.shared.getProviderStatus() // may be .notReady immediately after print("Current status:", status) } ``` -------------------------------- ### Configure Client-Level Logger for OpenFeature Swift SDK Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Assign a logger to a specific OpenFeature client to control logging for evaluations originating from that client. ```swift let client = OpenFeatureAPI.shared.getClient() let logger = Logger(label: "com.example.app.flags") client.setLogger(logger) ``` -------------------------------- ### Register Provider with OpenFeature Swift SDK Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Register a custom provider with the OpenFeature API. Ensure the provider is added as a dependency before registration. ```swift await OpenFeatureAPI.shared.setProviderAndWait(provider: MyProvider()) ``` -------------------------------- ### Implement Custom FeatureProvider in Swift Source: https://context7.com/open-feature/swift-sdk/llms.txt Implement the `FeatureProvider` protocol to integrate any feature flag backend. Delegate `status` and event publishing to `ProviderStatusTracker` for thread-safety and automatic status-replay. ```swift import Combine import OpenFeature import Logging final class RemoteConfigProvider: FeatureProvider { var hooks: [any Hook] = [] var metadata: ProviderMetadata = RemoteConfigMetadata() private let statusTracker = ProviderStatusTracker() var status: ProviderStatus { statusTracker.status } func observe() -> AnyPublisher { statusTracker.observe() } // Called once when the provider is registered with OpenFeatureAPI func initialize(initialContext: EvaluationContext?) -> Future { Future { [weak self] promise in guard let self else { return promise(.success(())) } // Fetch remote configuration URLSession.shared.dataTask(with: URL(string: "https://flags.example.com/config")!) { data, _, error in if let error { self.statusTracker.send(.error(ProviderEventDetails(errorMessage: error.localizedDescription))) } else { // Parse data and store locally... self.statusTracker.send(.ready(nil)) } promise(.success(())) }.resume() } } // Called whenever the global EvaluationContext changes func onContextSet(oldContext: EvaluationContext?, newContext: EvaluationContext) -> Future { Future { [weak self] promise in guard let self else { return promise(.success(())) } self.statusTracker.send(.reconciling(nil)) // Re-fetch flags for new targeting context... self.statusTracker.send(.contextChanged(nil)) promise(.success(())) } } func getBooleanEvaluation( key: String, defaultValue: Bool, context: EvaluationContext?, logger: Logger? ) throws -> ProviderEvaluation { // Look up key in locally-cached config guard let flagValue = localFlags[key] else { throw OpenFeatureError.flagNotFoundError(key: key) } logger?.debug("Resolved boolean flag \(key) = \(flagValue)") return ProviderEvaluation(value: flagValue as? Bool ?? defaultValue, variant: "remote", reason: Reason.targetingMatch.rawValue) } func getStringEvaluation(key: String, defaultValue: String, context: EvaluationContext?) throws -> ProviderEvaluation { ProviderEvaluation(value: localFlags[key] as? String ?? defaultValue) } func getIntegerEvaluation(key: String, defaultValue: Int64, context: EvaluationContext?) throws -> ProviderEvaluation { ProviderEvaluation(value: localFlags[key] as? Int64 ?? defaultValue) } func getDoubleEvaluation(key: String, defaultValue: Double, context: EvaluationContext?) throws -> ProviderEvaluation { ProviderEvaluation(value: localFlags[key] as? Double ?? defaultValue) } func getObjectEvaluation(key: String, defaultValue: Value, context: EvaluationContext?) throws -> ProviderEvaluation { ProviderEvaluation(value: defaultValue) } private var localFlags: [String: Any] = [:] } struct RemoteConfigMetadata: ProviderMetadata { var name: String? = "RemoteConfigProvider" } ``` -------------------------------- ### OpenFeatureAPI.shared.getClient() Source: https://context7.com/open-feature/swift-sdk/llms.txt Retrieves a Client instance bound to the global provider. Clients are used to resolve feature flags and can be anonymous or named. ```APIDOC ## OpenFeatureAPI.shared.getClient() ### Description Returns a `Client` instance bound to the global provider. Clients are lightweight objects that delegate all flag resolution to the registered provider through the hook pipeline. ### Usage ```swift Task { await OpenFeatureAPI.shared.setProviderAndWait(provider: MyProvider()) // Default anonymous client let client = OpenFeatureAPI.shared.getClient() // Named client (name appears in hook context and metadata) let namedClient = OpenFeatureAPI.shared.getClient(name: "checkout-service", version: "2.1.0") let isEnabled = client.getBooleanValue(key: "dark-mode", defaultValue: false) print("dark-mode:", isEnabled) // true (per MyProvider stub above) } ``` ``` -------------------------------- ### client.getValue / client.getDetails (Generic) Source: https://context7.com/open-feature/swift-sdk/llms.txt Generic type-safe methods for evaluating feature flags. The expected type is inferred from the `defaultValue` parameter, and `getDetails` allows for advanced options. ```APIDOC ## client.getValue(key:defaultValue:) / client.getDetails(key:defaultValue:) ### Description Generic type-safe evaluation methods. The type is inferred from the `defaultValue` parameter. ### Usage ```swift // Generic getValue — type inferred as Bool let isNewUI: Bool = client.getValue(key: "new-ui", defaultValue: false) // Generic getDetails with FlagEvaluationOptions let options = FlagEvaluationOptions( hooks: [MyAuditHook()], hookHints: ["user-tier": "premium"], logger: Logger(label: "com.app.flags") ) let details: FlagEvaluationDetails = client.getDetails( key: "cta-text", defaultValue: "Buy Now", options: options ) print(details.value, details.variant ?? "-", details.reason ?? "-") ``` ``` -------------------------------- ### Extract CocoaPods Trunk Token Source: https://github.com/open-feature/swift-sdk/blob/main/OWNERS.md After verifying your email, use this command to display the contents of your local .netrc file. The password field contains the trunk token. ```bash cat ~/.netrc ``` -------------------------------- ### Tracking User Actions Source: https://context7.com/open-feature/swift-sdk/llms.txt Associates a named user action with optional numeric value and structured attributes. Used to link conversion events back to feature flag evaluations for experimentation analysis. ```APIDOC ## client.track(key:details:) ### Description Associates a named user action with optional numeric value and structured attributes. Used to link conversion events back to feature flag evaluations for experimentation analysis. ### Method Swift SDK method ### Parameters - **key** (String) - Required - The name of the user action. - **context** (ImmutableContext) - Optional - The context for the tracking event. - **details** (ImmutableTrackingEventDetails) - Optional - Structured details for the tracking event, including a numeric value and attributes. ### Request Example ```swift Task { await OpenFeatureAPI.shared.setProviderAndWait(provider: MyProvider()) let client = OpenFeatureAPI.shared.getClient() // Simple event with no data client.track(key: "button-clicked") // Event with a numeric value (e.g., purchase amount) client.track( key: "purchase-completed", details: ImmutableTrackingEventDetails(value: 49.99) ) // Event with structured attributes and a numeric value let details = ImmutableTrackingEventDetails(value: 99.0) .withAttribute(key: "sku", value: .string("PRD-007")) .withAttribute(key: "currency", value: .string("USD")) .withAttributes(["channel": .string("mobile"), "experiment": .string("checkout-v2")]) client.track( key: "add-to-cart", context: ImmutableContext(targetingKey: "user-42"), details: details ) } ``` ``` -------------------------------- ### Configure Evaluation-Level Logger for OpenFeature Swift SDK Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Specify a logger for a single flag evaluation to isolate logging for critical or specific flag checks. This involves creating FlagEvaluationOptions with the desired logger. ```swift let logger = Logger(label: "com.example.app.critical-flag") let options = FlagEvaluationOptions(logger: logger) let value = client.getBooleanValue(key: "my-flag", defaultValue: false, options: options) ``` -------------------------------- ### client.getIntegerValue / client.getIntegerDetails Source: https://context7.com/open-feature/swift-sdk/llms.txt Resolves an integer feature flag. Supports `Int64` for values and provides detailed evaluation information when needed. ```APIDOC ## client.getIntegerValue(key:defaultValue:) / client.getIntegerDetails(key:defaultValue:) ### Description Resolves an integer (`Int64`) feature flag. ### Usage ```swift let maxRetries: Int64 = client.getIntegerValue(key: "max-retries", defaultValue: 3) let details: FlagEvaluationDetails = client.getIntegerDetails( key: "page-size", defaultValue: 20 ) print("Page size:", details.value) print("Is error:", details.errorCode != nil) // false on success ``` ``` -------------------------------- ### Add Hooks to OpenFeature Swift SDK Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Register hooks at different levels: globally for all evaluations, on a specific client, or for a single flag evaluation. ```swift // add a hook globally, to run on all evaluations OpenFeatureAPI.shared.addHooks(hooks: ExampleHook()) ``` ```swift // add a hook on this client, to run on all evaluations made by this client let client = OpenFeatureAPI.shared.getClient() client.addHooks(ExampleHook()) ``` ```swift // add a hook for this evaluation only _ = client.getValue( key: "key", defaultValue: false, options: FlagEvaluationOptions(hooks: [ExampleHook()])) ``` -------------------------------- ### client.getStringValue / client.getStringDetails Source: https://context7.com/open-feature/swift-sdk/llms.txt Resolves a string feature flag. Similar to boolean resolution, providing either the direct value or detailed evaluation information. ```APIDOC ## client.getStringValue(key:defaultValue:) / client.getStringDetails(key:defaultValue:) ### Description Resolves a string feature flag. Follows the same pattern as boolean resolution. ### Usage ```swift let theme: String = client.getStringValue(key: "ui-theme", defaultValue: "default") let details: FlagEvaluationDetails = client.getStringDetails( key: "api-endpoint-variant", defaultValue: "v1", options: FlagEvaluationOptions(hookHints: ["request-id": "abc-123"]) ) print("Resolved endpoint variant:", details.value) ``` ``` -------------------------------- ### client.getDoubleValue / client.getDoubleDetails Source: https://context7.com/open-feature/swift-sdk/llms.txt Resolves a double feature flag, suitable for numeric thresholds or percentages. Offers both direct value and detailed evaluation results. ```APIDOC ## client.getDoubleValue(key:defaultValue:) / client.getDoubleDetails(key:defaultValue:) ### Description Resolves a double feature flag for numeric thresholds, percentages, or experiment weights. ### Usage ```swift let rolloutPercentage: Double = client.getDoubleValue( key: "new-checkout-rollout", defaultValue: 0.0 ) let details: FlagEvaluationDetails = client.getDoubleDetails( key: "discount-rate", defaultValue: 0.0 ) print("Discount rate:", details.value, "Reason:", details.reason ?? "unknown") ``` ``` -------------------------------- ### Resolve String Feature Flag Source: https://context7.com/open-feature/swift-sdk/llms.txt Resolve a string feature flag using `getStringValue` or `getStringDetails`. The `getStringDetails` method allows passing `FlagEvaluationOptions` to customize evaluation, such as including hook hints. ```swift let theme: String = client.getStringValue(key: "ui-theme", defaultValue: "default") let details: FlagEvaluationDetails = client.getStringDetails( key: "api-endpoint-variant", defaultValue: "v1", options: FlagEvaluationOptions(hookHints: ["request-id": "abc-123"]) ) print("Resolved endpoint variant:", details.value) ``` -------------------------------- ### client.getObjectValue / client.getObjectDetails Source: https://context7.com/open-feature/swift-sdk/llms.txt Resolves a structured feature flag represented as a `Value` (JSON-like object). Can be decoded into custom `Decodable` models. ```APIDOC ## client.getObjectValue(key:defaultValue:) / client.getObjectDetails(key:defaultValue:) ### Description Resolves a structured `Value` flag (JSON-like nested object). Use `Value.decode()` to deserialize into a `Decodable` model. ### Usage ```swift struct FeatureConfig: Decodable { let maxItems: Int let enableSorting: Bool } let defaultConfig = Value.structure([ "maxItems": .integer(10), "enableSorting": .boolean(false) ]) let details: FlagEvaluationDetails = client.getObjectDetails( key: "feed-config", defaultValue: defaultConfig ) // Decode into a strongly-typed model if let config: FeatureConfig = try? details.value.decode() { print("Max items:", config.maxItems) print("Sorting enabled:", config.enableSorting) } ``` ``` -------------------------------- ### client.getBooleanValue / client.getBooleanDetails Source: https://context7.com/open-feature/swift-sdk/llms.txt Resolves a boolean feature flag. `getBooleanValue` returns the raw boolean value, while `getBooleanDetails` returns a `FlagEvaluationDetails` object with more information. ```APIDOC ## client.getBooleanValue(key:defaultValue:) / client.getBooleanDetails(key:defaultValue:) ### Description Resolves a boolean feature flag. The `Value` variant returns the bare Swift type; the `Details` variant returns a `FlagEvaluationDetails` containing the value, variant, reason, metadata, and any error information. ### Usage ```swift Task { await OpenFeatureAPI.shared.setProviderAndWait(provider: MyProvider()) let client = OpenFeatureAPI.shared.getClient() // Simple value resolution let showBanner: Bool = client.getBooleanValue(key: "show-promo-banner", defaultValue: false) // Rich details resolution let details: FlagEvaluationDetails = client.getBooleanDetails( key: "dark-mode", defaultValue: false ) print("Value:", details.value) // true print("Variant:", details.variant ?? "none") print("Reason:", details.reason ?? "none") print("Error:", details.errorCode as Any) // nil on success print("Metadata:", details.flagMetadata) } ``` ``` -------------------------------- ### Set Evaluation Context in OpenFeature Swift SDK Source: https://github.com/open-feature/swift-sdk/blob/main/README.md Configure and set the evaluation context for flag evaluations. This context can include targeting keys and custom attributes for dynamic criteria. ```swift let ctx = ImmutableContext( targetingKey: userId, structure: ImmutableStructure(attributes: ["product": Value.string(productId)])) OpenFeatureAPI.shared.setEvaluationContext(evaluationContext: ctx) ``` -------------------------------- ### Manage Provider Status with ProviderStatusTracker Source: https://context7.com/open-feature/swift-sdk/llms.txt Use `ProviderStatusTracker` to manage a provider's lifecycle status and publish events. This helper ensures thread-safety and automatic status-replay for new subscribers, satisfying OpenFeature specification requirements. ```swift // Inside a FeatureProvider implementation: private let statusTracker = ProviderStatusTracker() // Expose the tracker's computed status (thread-safe, frequently read from evaluation paths) var status: ProviderStatus { statusTracker.status } // Expose the tracker's publisher (replays current status to new subscribers) func observe() -> AnyPublisher { statusTracker.observe() } // Emit events during lifecycle methods; statusTracker updates status accordingly: // .ready → ProviderStatus.ready // .error → ProviderStatus.error // .error(code: .providerFatal) → ProviderStatus.fatal // .stale → ProviderStatus.stale // .reconciling → ProviderStatus.reconciling // .contextChanged → ProviderStatus.ready // .configurationChanged → (no status change, event still published) statusTracker.send(.ready(nil)) statusTracker.send(.error(ProviderEventDetails(errorCode: .general, errorMessage: "Network error"))) statusTracker.send(.configurationChanged(nil)) // Signals flags changed without changing status ``` -------------------------------- ### Resolve Integer Feature Flag Source: https://context7.com/open-feature/swift-sdk/llms.txt Resolve an integer feature flag using `getIntegerValue` or `getIntegerDetails`. The `getIntegerDetails` method returns detailed evaluation information, including whether an error occurred during resolution. ```swift let maxRetries: Int64 = client.getIntegerValue(key: "max-retries", defaultValue: 3) let details: FlagEvaluationDetails = client.getIntegerDetails( key: "page-size", defaultValue: 20 ) print("Page size:", details.value) print("Is error:", details.errorCode != nil) // false on success ``` -------------------------------- ### Resolve Boolean Feature Flag Source: https://context7.com/open-feature/swift-sdk/llms.txt Resolve a boolean feature flag using `getBooleanValue` for the direct value or `getBooleanDetails` for detailed evaluation information including variant, reason, and metadata. Ensure a provider is set before evaluation. ```swift Task { await OpenFeatureAPI.shared.setProviderAndWait(provider: MyProvider()) let client = OpenFeatureAPI.shared.getClient() // Simple value resolution let showBanner: Bool = client.getBooleanValue(key: "show-promo-banner", defaultValue: false) // Rich details resolution let details: FlagEvaluationDetails = client.getBooleanDetails( key: "dark-mode", defaultValue: false ) print("Value:", details.value) // true print("Variant:", details.variant ?? "none") print("Reason:", details.reason ?? "none") print("Error:", details.errorCode as Any) // nil on success print("Metadata:", details.flagMetadata) } ```