1"use strict";(globalThis.webpackChunkdocs||=[]).push([[1627],{85620(e,n,t){t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>l,default:()=>u,frontMatter:()=>o,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"reference/sdks/client/swift","title":"OpenFeature iOS SDK","description":"\x3c!--","source":"@site/docs/reference/sdks/client/swift.mdx","sourceDirName":"reference/sdks/client","slug":"/reference/sdks/client/swift","permalink":"/docs/reference/sdks/client/swift","draft":false,"unlisted":false,"editUrl":"https://github.com/open-feature/openfeature.dev/edit/main/docs/reference/sdks/client/swift.mdx","tags":[],"version":"current","frontMatter":{"title":"OpenFeature iOS SDK","slug":"swift","sidebar_label":"iOS"},"sidebar":"docs","previous":{"title":"Kotlin","permalink":"/docs/reference/sdks/client/kotlin"},"next":{"title":"Web","permalink":"/docs/reference/sdks/client/web/"}}');var i=t(74848),s=t(28453),a=t(73134);const o={title:"OpenFeature iOS SDK",slug:"swift",sidebar_label:"iOS"},l=void 0,d={},c=[{value:"Quick start",id:"quick-start",level:2},{value:"Requirements",id:"requirements",level:3},{value:"Install",id:"install",level:3},{value:"Xcode Dependencies",id:"xcode-dependencies",level:4},{value:"Swift Package Manager",id:"swift-package-manager",level:4},{value:"CocoaPods",id:"cocoapods",level:4},{value:"iOS Usage",id:"ios-usage",level:3},{value:"Features",id:"features",level:2},{value:"Providers",id:"providers",level:3},{value:"Targeting",id:"targeting",level:3},{value:"Hooks",id:"hooks",level:3},{value:"Tracking",id:"tracking",level:3},{value:"Logging",id:"logging",level:3},{value:"Configure Logger",id:"configure-logger",level:4},{value:"Provider Support",id:"provider-support",level:4},{value:"Domains",id:"domains",level:3},{value:"MultiProvider",id:"multiprovider",level:3},{value:"Basic Usage",id:"basic-usage",level:4},{value:"Evaluation Strategies",id:"evaluation-strategies",level:4},{value:"FirstMatchStrategy (Default)",id:"firstmatchstrategy-default",level:5},{value:"FirstSuccessfulStrategy",id:"firstsuccessfulstrategy",level:5},{value:"Use Cases",id:"use-cases",level:4},{value:"Eventing",id:"eventing",level:3},{value:"Shutdown",id:"shutdown",level:3},{value:"Extending",id:"extending",level:2},{value:"Develop a provider",id:"develop-a-provider",level:3},{value:"Status ownership",id:"status-ownership",level:4},{value:"Example implementation",id:"example-implementation",level:4},{value:"Develop a hook",id:"develop-a-hook",level:3}];function h(e){const n={a:"a",blockquote:"blockquote",code:"code",h2:"h2",h3:"h3",h4:"h4",h5:"h5",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsxs)("p",{align:"center",class:"github-badges",children:[(0,i.jsx)("a",{href:"https://github.com/open-feature/spec/releases/tag/v0.8.0",children:(0,i.jsx)("img",{alt:"Specification",src:"https://img.shields.io/static/v1?label=specification&message=v0.8.0&color=yellow&style=for-the-badge"})}),(0,i.jsx)("a",{href:"https://github.com/open-feature/swift-sdk/releases/tag/0.6.0",children:(0,i.jsx)("img",{alt:"Release",src:"https://img.shields.io/static/v1?label=release&message=v0.6.0&color=blue&style=for-the-badge"})}),(0,i.jsx)("br",{}),(0,i.jsx)("img",{alt:"Status",src:"https://img.shields.io/badge/lifecycle-alpha-a0c3d2.svg"})]}),"\n",(0,i.jsx)(n.h2,{id:"quick-start",children:"Quick start"}),"\n",(0,i.jsx)(a.A,{sdkTechnology:"swift"}),"\n",(0,i.jsx)(n.h3,{id:"requirements",children:"Requirements"}),"\n",(0,i.jsx)(n.p,{children:"This SDK supports the following Apple platforms:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.strong,{children:"iOS 15+"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.strong,{children:"macOS 12+"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.strong,{children:"watchOS 8+"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.strong,{children:"tvOS 15+"})}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["The SDK is built with Swift 5.5+ and uses Foundation, Combine, and the ",(0,i.jsx)(n.a,{href:"https://github.com/apple/swift-log",children:"swift-log"})," Logging package, making it suitable for all Apple platform contexts including mobile, desktop, wearable, and TV applications."]}),"\n",(0,i.jsx)(n.h3,{id:"install",children:"Install"}),"\n",(0,i.jsx)(n.h4,{id:"xcode-dependencies",children:"Xcode Dependencies"}),"\n",(0,i.jsx)(n.p,{children:"You have two options, both start from File > Add Packages... in the code menu."}),"\n",(0,i.jsxs)(n.p,{children:["First, ensure you have your GitHub account added as an option (+ > Add Source Control Account...). You will need to create a ",(0,i.jsx)(n.a,{href:"https://github.com/settings/tokens",children:"Personal Access Token"})," with the permissions defined in the Xcode interface."]}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:["Add as a remote repository","\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Search for ",(0,i.jsx)(n.code,{children:"[email protected]:open-feature/swift-sdk.git"}),' and click "Add Package"']}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["Clone the repository locally","\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Clone locally using your preferred method"}),"\n",(0,i.jsx)(n.li,{children:'Use the "Add Local..." button to select the local folder'}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Note:"})," Option 2 is only recommended if you are making changes to the client SDK."]}),"\n",(0,i.jsx)(n.h4,{id:"swift-package-manager",children:"Swift Package Manager"}),"\n",(0,i.jsx)(n.p,{children:"If you manage dependencies through SPM, in the dependencies section of Package.swift add:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:'.package(url: "[email protected]:open-feature/swift-sdk.git", from: "0.6.0")\n'})}),"\n",(0,i.jsx)(n.p,{children:"and in the target dependencies section add:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:'.product(name: "OpenFeature", package: "swift-sdk"),\n'})}),"\n",(0,i.jsx)(n.h4,{id:"cocoapods",children:"CocoaPods"}),"\n",(0,i.jsx)(n.p,{children:"If you manage dependencies through CocoaPods, add the following to your Podfile:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-ruby",children:"pod 'OpenFeature', '~> 0.5.0'\n"})}
1),"\n",(0,i.jsx)(n.p,{children:"Then, run:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"pod install\n"})}),"\n",(0,i.jsx)(n.h3,{id:"ios-usage",children:"iOS Usage"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:'import OpenFeature\n\nTask {\n let provider = CustomProvider()\n // configure a provider, wait for it to complete its initialization tasks\n await OpenFeatureAPI.shared.setProviderAndWait(provider: provider)\n\n // get a bool flag value\n let client = OpenFeatureAPI.shared.getClient()\n let flagValue = client.getBooleanValue(key: "boolFlag", defaultValue: false)\n}\n'})}),"\n",(0,i.jsx)(n.h2,{id:"features",children:"Features"}),"\n",(0,i.jsxs)(n.table,{children:[(0,i.jsx)(n.thead,{children:(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.th,{children:"Status"}),(0,i.jsx)(n.th,{children:"Features"}),(0,i.jsx)(n.th,{children:"Description"})]})}),(0,i.jsxs)(n.tbody,{children:[(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"\u2705"}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.a,{href:"#providers",children:"Providers"})}),(0,i.jsx)(n.td,{children:"Integrate with a commercial, open source, or in-house feature management tool."})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"\u2705"}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.a,{href:"#targeting",children:"Targeting"})}),(0,i.jsxs)(n.td,{children:["Contextually-aware flag evaluation using ",(0,i.jsx)(n.a,{href:"/docs/reference/concepts/evaluation-context",children:"evaluation context"}),"."]})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"\u2705"}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.a,{href:"#hooks",children:"Hooks"})}),(0,i.jsx)(n.td,{children:"Add functionality to various stages of the flag evaluation life-cycle."})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"\u2705"}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.a,{href:"#tracking",children:"Tracking"})}),(0,i.jsx)(n.td,{children:"Associate user actions with feature flag evaluations."})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"\u2705"}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.a,{href:"#logging",children:"Logging"})}),(0,i.jsx)(n.td,{children:"Integrate with popular logging packages."})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"\u274c"}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.a,{href:"#domains",children:"Domains"})}),(0,i.jsx)(n.td,{children:"Logically bind clients with providers."})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"\u2705"}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.a,{href:"#multiprovider",children:"MultiProvider"})}),(0,i.jsx)(n.td,{children:"Combine multiple providers with configurable evaluation strategies."})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"\u2705"}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.a,{href:"#eventing",children:"Eventing"})}),(0,i.jsx)(n.td,{children:"React to state changes in the provider or flag management system."})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"\u274c"}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.a,{href:"#shutdown",children:"Shutdown"})}),(0,i.jsx)(n.td,{children:"Gracefully clean up a provider during application shutdown."})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"\u2705"}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.a,{href:"#extending",children:"Extending"})}),(0,i.jsx)(n.td,{children:"Extend OpenFeature with custom providers and hooks."})]})]})]}),"\n",(0,i.jsx)("sub",{children:"Implemented: \u2705 | In-progress: \u26a0\ufe0f | Not implemented yet: \u274c"}),"\n",(0,i.jsx)(n.h3,{id:"providers",children:"Providers"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"/docs/reference/concepts/provider",children:"Providers"})," are an abstraction between a flag management system and the OpenFeature SDK.\nLook ",(0,i.jsx)(n.a,{href:"/ecosystem?instant_search%5BrefinementList%5D%5Btype%5D%5B0%5D=Provider&instant_search%5BrefinementList%5D%5Btechnology%5D%5B0%5D=Swift",children:"here"})," for a complete list of available providers.\nIf the provider you're looking for hasn't been created yet, see the ",(0,i.jsx)(n.a,{href:"#develop-a-provider",children:"develop a provider"})," section to learn how to build it yourself."]}
1),"\n",(0,i.jsx)(n.p,{children:"Once you've added a provider as a dependency, it can be registered with OpenFeature like this:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:"await OpenFeatureAPI.shared.setProviderAndWait(provider: MyProvider())\n"})}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsx)(n.p,{children:"Asynchronous API that doesn't wait is also available"}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"targeting",children:"Targeting"}),"\n",(0,i.jsxs)(n.p,{children:["Sometimes, the value of a flag must consider some dynamic criteria about the application or user, such as the user's location, IP, email address, or the server's location.\nIn OpenFeature, we refer to this as ",(0,i.jsx)(n.a,{href:"/specification/glossary#targeting",children:"targeting"}),".\nIf the flag management system you're using supports targeting, you can provide the input data using the ",(0,i.jsx)(n.a,{href:"/docs/reference/concepts/evaluation-context",children:"evaluation context"}),"."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:'// Configure your evaluation context and pass it to OpenFeatureAPI\nlet ctx = ImmutableContext(\n targetingKey: userId,\n structure: ImmutableStructure(attributes: ["product": Value.string(productId)]))\nOpenFeatureAPI.shared.setEvaluationContext(evaluationContext: ctx)\n'})}),"\n",(0,i.jsx)(n.h3,{id:"hooks",children:"Hooks"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"/docs/reference/concepts/hooks",children:"Hooks"})," allow for custom logic to be added at well-defined points of the flag evaluation life-cycle.\nLook ",(0,i.jsx)(n.a,{href:"/ecosystem/?instant_search%5BrefinementList%5D%5Btype%5D%5B0%5D=Hook&instant_search%5BrefinementList%5D%5Btechnology%5D%5B0%5D=Swift",children:"here"})," for a complete list of available hooks.\nIf the hook you're looking for hasn't been created yet, see the ",(0,i.jsx)(n.a,{href:"#develop-a-hook",children:"develop a hook"})," section to learn how to build it yourself."]}),"\n",(0,i.jsx)(n.p,{children:"Once you've added a hook as a dependency, it can be registered at the global, client, or flag invocation level."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:'// add a hook globally, to run on all evaluations\nOpenFeatureAPI.shared.addHooks(hooks: ExampleHook())\n\n// add a hook on this client, to run on all evaluations made by this client\nlet client = OpenFeatureAPI.shared.getClient()\nclient.addHooks(ExampleHook())\n\n// add a hook for this evaluation only\n_ = client.getValue(\n key: "key",\n defaultValue: false,\n options: FlagEvaluationOptions(hooks: [ExampleHook()]))\n'})}),"\n",(0,i.jsx)(n.h3,{id:"tracking",children:"Tracking"}),"\n",(0,i.jsxs)(n.p,{children:["The tracking API allows you to use OpenFeature abstractions and objects to associate user actions with feature flag evaluations.\nThis is essential for robust experimentation powered by feature flags.\nFor example, a flag enhancing the appearance of a UI component might drive user engagement to a new feature; to test this hypothesis, telemetry collected by a ",(0,i.jsx)(n.a,{href:"#hooks",children:"hook"})," or ",(0,i.jsx)(n.a,{href:"#providers",children:"provider"})," can be associated with telemetry reported in the client's ",(0,i.jsx)(n.code,{children:"track"})," function."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:'let client = OpenFeatureAPI.shared.getClient()\n\n// Track an event (uses the stored evaluation context on the client)\nclient.track(key: "test")\n\n// Track an event with a numeric value\nclient.track(key: "test-value", details: ImmutableTrackingEventDetails(value: 5))\n'})}),"\n",(0,i.jsxs)(n.p,{children:["Set evaluation context via provider initialization or ",(0,i.jsx)(n.code,{children:"OpenFeatureAPI.shared.setEvaluationContext(...)"})," before tracking. This client SDK follows the OpenFeature static-context tracking API and does not accept evaluation context at track invocation time."]}),"\n",(0,i.jsx)(n.p,{children:"Note that some providers may not support tracking; check the documentation for your provider for more information."}),"\n",(0,i.jsx)(n.h3,{id:"logging",children:"Logging"}),"\n",(0,i.jsxs)(n.p,{children:["The iOS SDK integrates with ",(0,i.jsx)(n.a,{href:"https://github.com/apple/swift-log",children:"swift-log"}),", the standard logging API for Swift. This provides a unified, cross-platform logging interface that works with any swift-log compatible backend."]}),"\n",(0,i.jsx)(n.h4,{id:"configure-logger",children:"Configure Logger"}),"\n",(0,i.jsx)(n.p,{children:"You can configure logging at three levels, with each level taking precedence over the previous:"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"1. Global (API-level)"})," - affects all flag evaluations:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:'import Logging\n\nlet logger = Logger(label: "com.example.app.openfeature")\nOpenFeatureAPI.shared.setLogger(logger)\n'})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"2. Client-level"})," - affects all evaluations from a specific client:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:'let
1client = OpenFeatureAPI.shared.getClient()\nlet logger = Logger(label: "com.example.app.flags")\nclient.setLogger(logger)\n'})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"3. Evaluation-level"})," - affects a single flag evaluation:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:'let logger = Logger(label: "com.example.app.critical-flag")\nlet options = FlagEvaluationOptions(logger: logger)\nlet value = client.getBooleanValue(key: "my-flag", defaultValue: false, options: options)\n'})}),"\n",(0,i.jsx)(n.h4,{id:"provider-support",children:"Provider Support"}),"\n",(0,i.jsx)(n.p,{children:"Providers can optionally use the logger for debugging and diagnostics. The logger is passed to providers during flag evaluation, allowing them to log relevant information."}),"\n",(0,i.jsx)(n.p,{children:"If no logger is configured, logging is disabled. The logger is completely optional for both SDK users and provider authors."}),"\n",(0,i.jsx)(n.h3,{id:"domains",children:"Domains"}),"\n",(0,i.jsx)(n.p,{children:"Domains allow you to logically bind clients with providers, enabling the use of multiple providers within a single application. Each domain can have its own provider, and clients can be associated with a specific domain."}),"\n",(0,i.jsx)(n.p,{children:"Support for domains is not yet available in the iOS SDK."}),"\n",(0,i.jsx)(n.h3,{id:"multiprovider",children:"MultiProvider"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"MultiProvider"})," allows you to combine multiple feature flag providers into a single provider, enabling you to use different providers for different flags or implement fallback mechanisms. This is useful when migrating between providers, implementing A/B testing across providers, or ensuring high availability."]}),"\n",(0,i.jsx)(n.h4,{id:"basic-usage",children:"Basic Usage"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:'import OpenFeature\n\nTask {\n // Create individual providers\n let primaryProvider = PrimaryProvider()\n let fallbackProvider = FallbackProvider()\n \n // Create a MultiProvider with default FirstMatchStrategy\n let multiProvider = MultiProvider(providers: [primaryProvider, fallbackProvider])\n \n // Set the MultiProvider as the global provider\n await OpenFeatureAPI.shared.setProviderAndWait(provider: multiProvider)\n \n // Use flags normally - the MultiProvider will handle provider selection\n let client = OpenFeatureAPI.shared.getClient()\n let flagValue = client.getBooleanValue(key: "my-flag", defaultValue: false)\n}\n'})}),"\n",(0,i.jsx)(n.h4,{id:"evaluation-strategies",children:"Evaluation Strategies"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"MultiProvider"})," supports different strategies for evaluating flags across multiple providers:"]}),"\n",(0,i.jsx)(n.h5,{id:"firstmatchstrategy-default",children:"FirstMatchStrategy (Default)"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"FirstMatchStrategy"}),' evaluates providers in order and returns the first result that doesn\'t indicate "flag not found". If a provider returns an error other than "flag not found", that error is returned immediately.']}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:"let multiProvider = MultiProvider(\n providers: [primaryProvider, fallbackProvider],\n strategy: FirstMatchStrategy()\n)\n"})}),"\n",(0,i.jsx)(n.h5,{id:"firstsuccessfulstrategy",children:"FirstSuccessfulStrategy"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"FirstSuccessfulStrategy"})," evaluates providers in order and returns the first successful result (no error). Unlike ",(0,i.jsx)(n.code,{children:"FirstMatchStrategy"}),', it continues to the next provider if any error occurs, including "flag not found".']}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:"let multiProvider = MultiProvider(\n providers: [primaryProvider, fallbackProvider],\n strategy: FirstSuccessfulStrategy()\n)\n"})}),"\n",(0,i.jsx)(n.h4,{id:"use-cases",children:"Use Cases"}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Provider Migration:"})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:"// Gradually migrate from OldProvider to NewProvider\nlet multiProvider = MultiProvider(providers: [\n NewProvider(), // Check new provider first\n OldProvider() // Fall back to old provider\n])\n"})}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"High Availability:"})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:"// Use multiple providers for redundancy\nlet multiProvider = MultiProvider(providers: [\n RemoteProvider(),\n LocalCacheProvider(),\n StaticProvider()\n])\n"})}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Environment-Specific Providers:"})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:'// Different providers for different environments\nlet providers = [\n EnvironmentProvider(environment: "production"),\n DefaultProvider()\n]\nlet multiProvider = MultiProvider(providers: providers)\n'})}),"\n",(0,i.jsx)(n.h3,{id:"eventing",children:"Eventing"}),"\n",(0,i.jsxs)(n.p,{children:["Events allow you to react to state changes in the provider or underlying flag management system, such as flag definition changes, provider readiness, or error conditions.\nInitialization events (",(0,i.jsx)(n.code,{children:"PROVIDER_READY"})," on success, ",(0,i.jsx)(n.code,{children:"PROVIDER_ERROR"})," on failure) are dispatched for every provider.\nSome providers support additional events, such as ",(0,i.jsx)(n.code,{children:"PROVIDER_CONFIGURATION_CHANGED"}),"."]}),"\n",(0,i.jsx)(n.p,{children:"Please refer to the documentation of the provider you're using to see what events are supported."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:"let cancellable = OpenFeatureAPI.shared.observe().sink { event in\n switch event {\n case ProviderEvent.ready:\n // ...\n default:\n // ...\n }\n}\n"})}),"\n",(0,i.jsx)(n.h3,{id:"shutdown",children:"Shutdown"}),"\n",(0,i.jsx)(n.p,{children:"A shutdown function is not yet available in the iOS SDK."}),"\n",(0,i.jsx)(n.h2,{id:"extending",children:"Extending"}),"\n",(0,i.jsx)(n.h3,{id:"develop-a-provider",children:"Develop a provider"}),"\n",(0,i.jsxs)(n.p,{children:["To develop a provider, you need to create a new project and include the OpenFeature SDK as a dependency.\nYou'll then need to write the provider by implementing the ",(0,i.jsx)(n.code,{children:"FeatureProvider"})," interface exported by the OpenFeature SDK."]}),"\n",(0,i.jsx)(n.h4,{id:"status-ownership",children:"Status ownership"}),"\n",(0,i.jsxs)(n.p,{children:["Providers are fully responsible for managing their own status. The SDK reads ",(0,i.jsx)(n.code,{children:"status"})," from the\nprovider but never sets it. You must keep ",(0,i.jsx)(n.code,{children:"status"})," consistent with the events you emit, and the\nproperty must be thread-safe (it c
1an be read concurrently from flag evaluation paths)."]}),"\n",(0,i.jsxs)(n.p,{children:["The easiest way to satisfy these requirements is to delegate to ",(0,i.jsx)(n.code,{children:"ProviderStatusTracker"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"example-implementation",children:"Example implementation"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:"import Combine\nimport OpenFeature\n\nfinal class CustomProvider: FeatureProvider {\n var hooks: [any Hook] = []\n var metadata: ProviderMetadata = CustomMetadata()\n\n // ProviderStatusTracker keeps `status` in sync with emitted events,\n // handles thread safety, and replays the current status to new subscribers.\n private let statusTracker = ProviderStatusTracker()\n var status: ProviderStatus { statusTracker.status }\n func observe() -> AnyPublisher<ProviderEvent, Never> { statusTracker.observe() }\n\n func initialize(initialContext: EvaluationContext?) -> Future<Void, Never> {\n Future { promise in\n // Perform context-aware initialisation, then emit any non-.notReady status.\n // .ready and .error are the most common outcomes.\n self.statusTracker.send(.ready(nil))\n promise(.success(()))\n }\n }\n\n func onContextSet(oldContext: EvaluationContext?, newContext: EvaluationContext) -> Future<Void, Never> {\n // Note: this may be called again before a previous lifecycle Future has\n // resolved. Cancel any in-flight async work when a new call arrives.\n Future { promise in\n self.statusTracker.send(.reconciling(nil))\n // ... re-initialize with new context ...\n self.statusTracker.send(.contextChanged(nil)) // or .error(nil) on failure\n promise(.success(()))\n }\n }\n\n func getBooleanEvaluation(\n key: String,\n defaultValue: Bool,\n context: EvaluationContext?\n ) throws -> ProviderEvaluation<Bool> {\n // resolve a boolean flag value\n }\n\n ...\n}\n\n"})}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsxs)(n.p,{children:["Built a new provider? ",(0,i.jsx)(n.a,{href:"https://github.com/open-feature/openfeature.dev/issues/new?assignees=&labels=provider&projects=&template=document-provider.yaml&title=%5BProvider%5D%3A+",children:"Let us know"})," so we can add it to the docs!"]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"develop-a-hook",children:"Develop a hook"}),"\n",(0,i.jsxs)(n.p,{children:["To develop a hook, you need to create a new project and include the OpenFeature SDK as a dependency.\nImplement your own hook by conforming to the ",(0,i.jsx)(n.code,{children:"Hook interface"}),".\nTo satisfy the interface, all methods (",(0,i.jsx)(n.code,{children:"Before"}),"/",(0,i.jsx)(n.code,{children:"After"}),"/",(0,i.jsx)(n.code,{children:"Finally"}),"/",(0,i.jsx)(n.code,{children:"Error"}),") need to be defined."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",children:"class BooleanHook: Hook {\n typealias HookValue = Bool\n\n func before<HookValue>(ctx: HookContext<HookValue>, hints: [String: Any]) {\n // do something\n }\n\n func after<HookValue>(ctx: HookContext<HookValue>, details: FlagEvaluationDetails<HookValue>, hints: [String: Any]) {\n // do something\n }\n\n func error<HookValue>(ctx: HookContext<HookValue>, error: Error, hints: [String: Any]) {\n // do something\n }\n\n func finally<HookValue>(ctx: HookContext<HookValue>, details: FlagEvaluationDetails<HookValue>, hints: [String: Any]) {\n // do something\n }\n}\n"})}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsxs)(n.p,{children:["Built a new hook? ",(0,i.jsx)(n.a,{href:"https://github.com/open-feature/openfeature.dev/issues/new?assignees=&labels=hook&projects=&template=document-hook.yaml&title=%5BHook%5D%3A+",children:"Let us know"})," so we can add it to the docs!"]}),"\n"]})]})}function u(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},73134(e,n,t){t.d(n,{A:()=>o});var r=t(96540),i=t(28774),s=t(83457),a=t(74848);function o({sdkTechnology:e}){const[n,t]=(0,r.useState)("idle"),[o,l]=(0,r.useState)({});return(0,a.jsxs)("details",{className:"alert alert--info",style:{marginBottom:"1rem"},children:[(0,a.jsxs)("summary",{className:"mcp-install-summary",children:[(0,a.jsx)("strong",{children:"MCP Install"}),(0,a.jsx)("button",{className:"mcp-install-button",onClick:async n=>{n.preventDefault(),n.stopPropagation();let r="Install OpenFeature into this app";
1if(e)if(o[e])r=o[e];else{t("loading");try{const n=await fetch(`https://raw.githubusercontent.com/open-feature/mcp/refs/heads/main/prompts/${e}.md`);if(!n.ok)return console.error(`Failed to fetch prompt for ${e}: ${n.status}`),t("error"),void setTimeout((()=>t("idle")),2e3);{const t=await n.text();r=t,l((n=>({...n,[e]:t})))}}catch(i){return console.error(`Error fetching prompt for ${e}:`,i),t("error"),void setTimeout((()=>t("idle")),2e3)}}try{await navigator.clipboard.writeText(r),t("success"),setTimeout((()=>t("idle")),2e3)}catch(i){console.error("Failed to copy to clipboard:",i),t("error"),setTimeout((()=>t("idle")),2e3)}},style:{marginBottom:0},disabled:"loading"===n,children:(0,a.jsx)("span",{children:(()=>{switch(n){case"loading":return"\u23f3 Fetching...";case"success":return"\u2713 Copied!";case"error":return"\u26a0 Error";default:return"\ud83d\udccb Copy Prompt"}})()})})]}),(0,a.jsxs)("div",{style:{marginTop:"1rem"},children:[(0,a.jsxs)("p",{children:["Follow the ",(0,a.jsx)(i.A,{to:"/docs/reference/other-technologies/mcp",children:"MCP Getting Started"})," guide to quickly set up the OpenFeature MCP server and connect your AI tool."]}),(0,a.jsx)("ul",{children:(0,a.jsxs)("li",{children:["Run this prompt: ",(0,a.jsx)("code",{children:'"Install OpenFeature into this app"'})]})}),(0,a.jsx)("p",{style:{marginTop:"1rem"},children:(0,a.jsx)("strong",{children:"Quick Install:"})}),(0,a.jsxs)("div",{style:{display:"flex",gap:"10px",marginBottom:"1rem"},children:[(0,a.jsx)("a",{href:"cursor://anysphere.cursor-deeplink/mcp/install?name=OpenFeature&config=eyJjb21tYW5kIjogIm5weCIsICJhcmdzIjogWyIteSIsICJAb3BlbmZlYXR1cmUvbWNwIl19Cg==",className:"mcp-install-button",target:"_blank",rel:"noopener noreferrer",style:{marginBottom:0},children:"\ud83d\udce6 Install in Cursor"}),(0,a.jsx)("a",{href:"https://vscode.dev/redirect/mcp/install?name=OpenFeature&config=%7B%22command%22%3A%20%22npx%22%2C%20%22args%22%3A%20%5B%22-y%22%2C%20%22%40openfeature%2Fmcp%22%5D%7D",className:"mcp-install-button",target:"_blank",rel:"noopener noreferrer",style:{marginBottom:0},children:"\ud83d\udce6 Install in VS Code"})]}),(0,a.jsx)(s.A,{language:"bash",children:"claude mcp add --transport stdio openfeature npx -y @openfeature/mcp"})]})]})}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.