Featurevisor

Migrating the Swift SDK for Featurevisor v4

Upgrade Swift applications to SDK 4.0.0 for Featurevisor v4 support.

Follow the shared migration order: prepare canonical prerequisite datafiles, upgrade consumers, then upgrade the project CLI. Before publishing new runtime fields, follow Adopt new syntax safely.

Convert Groups before upgrading

This SDK silently ignores legacy feature ranges, with no compatibility handling or warning. Convert Groups to flat list requirements while still on the previous compatible SDK and v3 CLI, then publish all datafiles, caches and bundled copies before upgrading. Unconverted files can change cohorts or enable features without a variation. Old cohorts are not guaranteed. Follow Groups removed.

Minimal migration path

Most Swift application code remains unchanged. These APIs were already available in the previous SDK line:

  • createFeaturevisor(...) and FeaturevisorOptions
  • isEnabled(...), getVariation(...), and feature variable getters
  • global variable getters using a single variable key
  • evaluateFlag(...), evaluateVariation(...), and evaluateVariable(...)
  • stickyFeatures, stickyVariables, setStickyFeatures(...), and setStickyVariables(...)
  • getFeatureEvaluations(...), getVariableEvaluations(...), and child instances
  • sticky_features_set and sticky_variables_set events

If an application already uses canonical module callbacks and does not inspect legacy evaluation properties or construct old requirement model types, its source migration can be limited to selecting a compatible package release and testing its datafiles.

See the Swift SDK guide for the complete current API.

Breaking changes

Root condition reason Breaking

An unmet root gate reports conditions_unmet for feature flags and global variables, replacing the v3 required reason for feature flags and required_features_unmet for global variables. This applies to attribute, segment, feature and variable leaves in root conditions. Update callbacks, analytics and test expectations. Variation and feature variable evaluations retain their disabled reasons. See the shared migration guidance.

Before
let unmetRequirement = evaluation.reason == .required
After
let unmetRequirement = evaluation.reason == .conditionsUnmet

Datafile update result Breaking

setDatafile now returns true when it accepts the update, including unchanged content, and false when it rejects the update or the instance is closed. A rejected update keeps the previous datafile and reports details through diagnostics. Calls that ignore the result keep working. Update any wrapper or interface that declares the old return type. Merging remains the default.

Before
f.setDatafile(datafile)
After
let accepted = f.setDatafile(datafile)
if !accepted {
print("Datafile rejected. Check diagnostics.")
}

Module callbacks Breaking

The deprecated feature only before and after initializer arguments are removed. beforeEvaluation and afterEvaluation were already available in the previous SDK line, so this source change can be made before the breaking upgrade.

Before migration
let module = FeaturevisorModule(
name: "context",
before: { options in
options
},
bucketValue: { $0.bucketValue },
after: { evaluation, _ in evaluation }
)
let f = createFeaturevisor(FeaturevisorOptions(datafile: datafile, modules: [module]))
After migration
let module = FeaturevisorModule(
name: "context",
beforeEvaluation: { options in
options
},
bucketValue: { $0.bucketValue },
afterEvaluation: { evaluation, _ in evaluation }
)
let f = createFeaturevisor(FeaturevisorOptions(datafile: datafile, modules: [module]))

The canonical callbacks run for feature and global variable evaluations, including prerequisite evaluations. If callback behaviour is specific to features, inspect options.featureKey before applying it. setup, bucketKey, bucketValue, and close are unchanged.

Evaluation details Breaking

The legacy required and sticky properties are removed from Evaluation:

Before migration
let evaluation = f.evaluateVariable("checkout", "colour")
let prerequisites = evaluation.required
let sticky = evaluation.sticky
After migration
let evaluation = f.evaluateVariable("checkout", "colour")
let prerequisites = evaluation.conditions
let sticky = evaluation.stickyFeature

Check evaluation.reason == .sticky to identify sticky results. stickyFeature is optional detail and is not present for every sticky evaluation.

Accumulating global variable evaluations add variableOverridePaths, which records every matched authored path in execution order. The existing singular override properties identify the final match. Failed evaluated variable prerequisites use the new .conditionsUnmet reason.

Requirement model changes Breaking

The old runtime datafile model represented legacy required entries with RequiredValue and RequiredWithVariation. Both types are removed.

Previous modelAfter migration
RequiredValue.key(featureKey)RequiredFeature.feature(featureKey)
RequiredValue.withVariation(...) and RequiredWithVariationNo direct construction replacement. Author { feature, variation } under conditions and decode the generated datafile

Use the unified conditions grammar for all leaf kinds and groups. Applications that decode CLI generated datafiles do not need to construct prerequisite expression models themselves.

Language differences

Check bundled datafiles and deployment targets across Apple platforms before selecting the compatible package. The OpenFeature package minimums are described below.

OpenFeature

FeaturevisorOpenFeature is a separate product in the featurevisor-swift2 package. Set the package requirement to from: "4.0.0" and use the matching Featurevisor and FeaturevisorOpenFeature products.

The provider initializer and key mapping remain unchanged, but its OpenFeature Swift SDK dependency moves from 0.5 to 0.7, resolving to 0.7.0. The provider implements the status and event API introduced in 0.6, including ProviderStatusTracker, and returns Future<Void, Never> from lifecycle methods. Test provider readiness, invalid datafile status, recovery, configuration changes and shutdown through OpenFeature after upgrading. The updated provider maps .conditionsUnmet to OpenFeature's disabled reason.

Swift's upstream evaluation context returns the same empty string for an omitted targeting key and an explicit empty key. The provider cannot tell them apart and must preserve an existing userId or configured field on omission. To request an empty field, set that field itself to "" in context attributes. Clear any inherited nonempty targeting key at its source first.

Reason strings

OpenFeature 0.7 changes Reason raw values to specification strings. Update string comparisons, stored reasons and test expectations. Enum cases such as .targetingMatch and .error keep their names.

Before
Reason.targetingMatch.rawValue // "targetingMatch"
Reason.error.rawValue // "error"
After
Reason.targetingMatch.rawValue // "TARGETING_MATCH"
Reason.error.rawValue // "ERROR"

Logging

OpenFeature logging APIs now accept (any OpenFeatureLogger)? instead of Logging.Logger. This includes setLogger, FlagEvaluationOptions(logger:), MultiProvider(logger:) and logger parameters in custom provider evaluation methods. Use your own OpenFeatureLogger implementation or add the optional OpenFeatureSwiftLog product and wrap a swift-log logger with SwiftLogLogger.

Before
import Logging
import OpenFeature
let logger = Logger(label: "checkout")
OpenFeatureAPI.shared.setLogger(logger)
After
import Logging
import OpenFeature
import OpenFeatureSwiftLog
let logger = Logger(label: "checkout")
OpenFeatureAPI.shared.setLogger(SwiftLogLogger(logger))

Package installation

OpenFeature 0.7 removes upstream CocoaPods support. Featurevisor already uses Swift Package Manager and does not ship a podspec. Keep using the Featurevisor and FeaturevisorOpenFeature products above.

The package needs Swift 6.1 or later, which Xcode 16.3 and later include. Upgrade Xcode before the package if your applications or CI still build with an earlier release.

The package minimums rise to iOS 15, macOS 12, tvOS 15, and watchOS 8. These minimums apply even when an application selects only the core Featurevisor product. Core does not link OpenFeature, but Swift Package Manager still resolves the package dependency. Check every application and test target's deployment setting before selecting the new package release.

See Swift OpenFeature usage and the shared OpenFeature provider guide.

Migration checklist

Required for the migration

  • Prepare and deploy canonical conditions datafiles before changing the runtime.
  • Replace module before and after arguments with the canonical evaluation callbacks.
  • Replace legacy evaluation properties and remove old requirement model usage.
  • Update both Featurevisor package products together when used.
  • Build with Swift 6.1 or later, and check the raised package platform minimums and the OpenFeature 0.7 reason strings, logging and provider lifecycle.
  • Test feature, variation, feature variable, global variable, sticky, child, module, and bundled fallback behaviour.
  • Upgrade the project CLI last, review mutation order and date and regex comparisons, and retain a compatible rollback datafile.

Only if adopting optional features

Follow Adopt new syntax safely before publishing new datafile syntax. Upgrade every CLI runner before adding checks or scenarios.