Disclaimer: This website requires Please enable JavaScript in your browser settings for the best experience.

Dev guideRecipesAPI ReferenceChangelog
Dev guideAPI ReferenceRecipesChangelogUser GuideGitHubDev CommunityOptimizely AcademySubmit a ticketLog In
Dev guide

Decide methods for the Swift SDK

Overview of the decide methods which you can use to return a flag decision for a user in Optimizely Feature Experimentation.

Use the decide methods to return feature flag decisions for a user. Each decision includes whether the flag is enabled and which variation the user receives.

Decide

Returns a decision for a specific feature flag and user.

Minimum SDK version – v3.7+

Description

The decide method handles the variation decision for an individual experiment. It returns a decision result for a flag key for a user in an OptimizelyDecision object and contains the data required to deliver the flag rule.

This method ensures that Feature Experimentation properly buckets the user in experiment analytics, and you should use it if there is no need for pre-rendering decisions.

📘

Note

Decide is a method of the UserContext object. See OptimizelyUserContext for details.

See the OptimizelyDecision for details of the returned decision object.

Parameters

The following table describes parameters for the decide method:

ParameterTypeDescription
flagKeyStringThe key of the feature flag
options (optional)ArrayArray of OptimizelyDecideOption enums.

Returns

The decide method returns an OptimizelyDecision object. For more information, see OptimizelyDecision.

If the method encounters a critical error (SDK not ready, invalid flag key, and so on), then it returns a decision with a null Variation Key field and populates the Reasons field with error messages (regardless of the OptimizelyDecideOption.includeReasons option).

Example decide

// Create the user and decide which flag rule and variation they bucket into 
let user = optimizelyClient.createUserContext(userId: "user123", attributes: ["logged_in": true])
let decision = user.decide(key: "product_sort")

// Did the decision fail with a critical error?
guard let variationKey = decision.variationKey else {
  print("[decide] error: \(decision.reasons)")
	return
}

// The flag enabled state
let enabled: Bool = decision.enabled

// String variable value
let value1: String? = decision.variables.getValue(jsonPath: "sort_method")
// or:
let value2: String? = decision.variables.toMap()["sort_method"] as? String

// All variable values
let allVarValues: OptimizelyJSON = decision.variables

// The variation key. If null, decision fail with a critical error
let variationKey: String = decision.variationKey

// Flag decision reasons
let reasons: [String] = decision.reasons

// The user for which the decision was made
let userContext: OptimizelyUserContext = decision.userContext

Side effects

Invokes the decision notification listener if this listener is enabled.

Decide all

Returns decisions for all active (unarchived) flags for a user.

Minimum SDK Version – v3.6+

Description

Use the decide all method to retrieve decisions for all active flags before rendering content. This is particularly useful when you need to serve the correct cached version of your page or component based on the user's variation. For example when using an edge worker or cloud function.

Key features

  • Pre-rendering decision – Lets you know all variation assignments ahead of time.
  • Cache control – Lets you serve the correct cached content based on the user's pre-determined variations.
  • Delay experiment tracking – Use DISABLE_DECISION_EVENT to prevent the SDK from recording a decision before the user sees the feature. This ensures that participation is only tracked when the experience is delivered.
  • CMAB support – For CMAB experiments (SDK v5.2.0+), decisions are automatically cached to reduce latency. The cache is invalidated when CMAB attributes change or TTL expires. Use CMAB-specific decide options (ignoreCmabCache, invalidateUserCmabCache, resetCmabCache) to override default caching behavior. See CMAB cache control below.

Parameters

The following table describes parameters for the decide all method:

ParameterTypeDescription
options (optional)ArrayArray of OptimizelyDecideOption enums.

Returns

The decide all method returns a map of OptimizelyDecisions. For more information, see OptimizelyDecision.

If the method fails for all flags (for example, the SDK is not ready or the user context is invalid), then it returns an empty map. If the method detects an error for a specific flag, it returns error messages in the reasons field of the decision for that flag.

Example decideAll

// make decisions for all active (unarchived) flags in the project for a user
let decisions = user.decideAll()
// or only for enabled flags
let decisions = user.decideAll(options: [.enabledFlagsOnly])

let flagKeys = decisions.keys
let flagDecisions = decisions.values
let decisionForFlag1 = decisions["flag_1"]

Side effects

Invokes the decision notification listener for each decision if this listener is enabled.

Decide for keys

Returns a map of flag decisions for specified flag keys.

Minimum SDK version – v3.6+

Description

Returns a map of decisions for the specified flag keys.

Parameters

The following table describes parameters for the decide for keys method:

ParameterTypeDescription
keysArrayArray of string flag keys.
options (optional)ArrayArray of OptimizelyDecideOption enums.

Returns

Returns a map of OptimizelyDecisions. For more information, see OptimizelyDecision.

If the method fails for all flags (for example, the SDK is not ready or the user context is invalid), then it returns an empty map. If the method detects an error for a specific flag, it returns error messages in the reasons field of the decision for that flag.

Example decide(keys)

The following is an example of getting specified flags for the user:

// make a decisions for specific enabled  flags
let decisions = user.decide(keys: ["flag_1", "flag_2"], options: [.enabledOnly])

let decisionForFlag1 = decisions["flag_1"]
let decisionForFlag2 = decisions["flag_2"]

Side effects

Invokes the decision notification listener for each decision if this listener is enabled.

Decide Async

The decide async methods are the asynchronous version of the synchronous decide methods. Each decision includes whether the flag is enabled and which variation the user receives.

Fetch decisions asynchronously for all active (unarchived) flags for a user.

Minimum SDK version – v5.2.0+

Example decideAsync

let user = optimizelyClient.createUserContext(userId: "user123", attributes: ["logged_in": true])
let decision = user.decideAsync(key: "product_sort") { decision in
    // Did the decision fail with a critical error?
    guard let variationKey = decision.variationKey else {
        print("[decide] error: \(decision.reasons)")
        return
    }

    // The flag enabled state
    let enabled: Bool = decision.enabled

    // String variable value
    let value1: String? = decision.variables.getValue(jsonPath: "sort_method")
    // or:
    let value2: String? = decision.variables.toMap()["sort_method"] as? String

    // All variable values
    let allVarValues: OptimizelyJSON = decision.variables

		// The variation key. If null, decision fail with a critical error
		let variationKey: String = decision.variationKey

    // Flag decision reasons
    let reasons: [String] = decision.reasons

    // The user for which the decision was made
    let userContext: OptimizelyUserContext = decision.userContext
}
Task {
    // Create the user and decide which flag rule and variation they bucket into
    let user = optimizelyClient.createUserContext(userId: "user123", attributes: ["logged_in": true])
    let decision = await user.decideAsync(key: "product_sort")
    
    // Did the decision fail with a critical error?
    guard let variationKey = decision.variationKey else {
        print("[decide] error: \(decision.reasons)")
        return
    }
    
    // The flag enabled state
    let enabled: Bool = decision.enabled
    
    // String variable value
    let value1: String? = decision.variables.getValue(jsonPath: "sort_method")
    // or:
    let value2: String? = decision.variables.toMap()["sort_method"] as? String
    
    // All variable values
    let allVarValues: OptimizelyJSON = decision.variables
    
		// The variation key. If null, decision fail with a critical error
		let variationKey: String = decision.variationKey
    
    // Flag decision reasons
    let reasons: [String] = decision.reasons
    
    // The user for which the decision was made
    let userContext: OptimizelyUserContext = decision.userContext
}

Example decideAllAsync

let user = optimizelyClient.createUserContext(userId: "user123", attributes: ["logged_in": true])
user.decideAllAsync() { decisions in }
// or only for enabled flags
user.decideAllAsync(options: [.enabledFlagsOnly]) { decisions in
    let flagKeys = decisions.keys
    let flagDecisions = decisions.values
    let decisionForFlag1 = decisions["flag_1"]
}
Task {
    let user = optimizelyClient.createUserContext(userId: "user123", attributes: ["logged_in": true])
    _ = await user.decideAllAsync()
    // or only for enabled flags
    let decisions = await user.decideAllAsync(options: [.enabledFlagsOnly])
    let flagKeys = decisions.keys
    let flagDecisions = decisions.values
    let decisionForFlag1 = decisions["flag_1"]
}

Example decideAsync(keys)

// make decisions for flags
user.decideAsync(keys: ["flag_1", "flag_2"]) { decisions in }
// or only for enabled flags
user.decideAsync(keys: ["flag_1", "flag_2"], options: [.enabledFlagsOnly]) { decisions in
    let flagKeys = decisions.keys
    let flagDecisions = decisions.values
    let decisionForFlag1 = decisions["flag_1"]
    let decisionForFlag2 = decisions["flag_2"]
}
Task {
    let user = optimizelyClient.createUserContext(userId: "user123", attributes: ["logged_in": true])
    // make decisions for flags
    _ = await user.decideAsync(keys: ["flag_1", "flag_2"])
    // or only for enabled flags
    let decisions = await user.decideAsync(keys: ["flag_1", "flag_2"], options: [.enabledFlagsOnly])
    let flagKeys = decisions.keys
    let flagDecisions = decisions.values
    let decisionForFlag1 = decisions["flag_1"]
    let decisionForFlag2 = decisions["flag_2"]
}

OptimizelyDecideOption

The following table lists the OptimizelyDecideOption enum with an explanation what happens if you set them. In addition to setting these options individually for a decide method, you can also set them as global defaults when you instantiate the Optimizely client. See Initialize Swift SDK.

OptimizelyDecideOption

enum

If set

OptimizelyDecideOption.disableDecisionEvent

Prevents the SDK from dispatching an impression event when serving a variation. This disables decision tracking on the Optimizely Experiment Results page and the decision notification listener.

OptimizelyDecideOption.enabledFlagsOnly

Returns decisions only for flags that are currently enabled. Used with the decide all method and decide for keys method.

When this option is not set, the Android SDK returns all decisions regardless of whether the flag is enabled.

OptimizelyDecideOption.ignoreUserProfileService

Bypasses the user profile service (both lookup and save) for the decision.

When this option is not set, user profile service overrides audience targeting, traffic allocation, and experiment mutual exclusion groups.

OptimizelyDecideOption.includeReasons

Adds log messages to the reasons field of the decision. Critical errors are always returned, even if this option is not set.

OptimizelyDecideOption.excludeVariables

Excludes flag variable values from the decision result. Use this option to minimize the returned decision by skipping large JSON variables.

OptimizelyDecideOption.ignoreCmabCache

Bypasses the CMAB cache and fetches a fresh decision from the CMAB service. Use when you need real-time decisions that reflect the latest context. Available in SDK v5.2.0+.

OptimizelyDecideOption.invalidateUserCmabCache

Removes the cached CMAB decision for this user and experiment before making the decision. Use when user context has changed significantly and you want to ensure a fresh decision. Available in SDK v5.2.0+.

OptimizelyDecideOption.resetCmabCache

Clears all entries from the CMAB cache before making the decision. Use sparingly for testing or cache corruption scenarios. Available in SDK v5.2.0+.

The following code sample shows how to implement the OptimizelyDecideOption as a global default and locally in a decide method call.

// Set global default decide options when initializing the client
let optimizely = OptimizelyClient(sdkKey: sdkKey, defaultDecideOptions: [.disableDecisionEvent])


// Set additional options in a decide call
let user = optimizely.createUserContext(userId: "user123")
let decisions = user.decideAll(options: [.enabledFlagsOnly, .disableDecisionEvent])

Best practices

  • Early and late invocation
    • Use the decide all method early in your application's rendering process to load the correct cached content. For example, on the CDN with an Edge SDK.
    • Use the decide method at the point of user interaction or when you must ensure Feature Experimentation records the decision in your experiment analytics.
  • Combining methods – When using the decide and decide all methods, always pair the decide all method with the decide method for each experiment the user encounters. This prevents discrepancies between served content and analytics data.
  • Parameter management – Ensure you use the DISABLE_DECISION_EVENT option with the decide all method to avoid premature bucketing, then use the decide method to handle the decision event when the user experiences the content.

CMAB cache control

👍

Beta

CMAB for Feature Experimentation is in beta. Contact your Customer Success Manager for more information.

Minimum SDK version – v5.2.0+

For Contextual Multi-Armed Bandit (CMAB) experiments, the Swift SDK automatically caches variation assignments to reduce latency and API calls. The cache stores decisions based on user ID, experiment ID, and CMAB attribute values.

Automatic cache invalidation

The cache is automatically invalidated when

  • The cached entry's TTL expires (default: 30 minutes).
  • CMAB attribute values change for a user (detected via attribute hash comparison).

Manual cache control

Use the following CMAB-specific decide options to control cache behavior on a per-decision basis:


// Initialize the Optimizely client
let client = OptimizelyClient(sdkKey: <YOUR_SDK_KEY>)

// Example 1: Use default cmab and caching logic
Task {
    try await client.start()
    let user = OptimizelyUserContext(
        optimizely: client,
        userId: "user123",
        attributes: ["country": "us", "age" : 25]
    )
    let decision =  await user.decideAsync(key: "cmab_flag_key")
}

// Example 2: Invalidate cache when user context changes significantly
Task {
    let updatedUser = OptimizelyUserContext(
        optimizely: client,
        userId: "user123",
        attributes: ["location": "UK", "age" : 40]
    )
    let options: [OptimizelyDecideOption] = [.invalidateUserCmabCache,]
    let decision =  await user.decideAsync(key: "cmab_flag_key", options: options)
}

// Example 3: Reset entire CMAB cache (use sparingly)
Task {
    let options: [OptimizelyDecideOption] = [.resetCmabCache,]
    let decision =  await user.decideAsync(key: "cmab_flag_key", options: options)
}
🚧

Important

To retrieve a CMAB decision, you must use the decideAsync method. The decide method does not fetch CMAB decisions to avoid blocking the user.

CMAB decision reasons

When using includeReasons, CMAB-related information displays in the decision's reasons field.

Task {
    let options: [OptimizelyDecideOption] = [.includeReasons,]
    let decision =  await user.decideAsync(key: "cmab_flag_key", options: options)
    print("[decide] reasons: \(decision.reasons)")
    // Examples:
    // User (user123) does not fall into cmab traffic allocation in experiment (exp_1).
    // CMAB is not supported in sync mode.
    // Failed to fetch CMAB data for experiment (exp_2).
}

Configure cache settings

import Optimizely

// Create CMAB config with custom cache size, timeout and prediction endpoint
var cmabConfig = CmabConfig(
    cacheSize: 50, // Set cache size (default: 100)
    cacheTimeoutInSecs: 120, // Set TTL in seconds (default: 1800 = 30 minutes)
    predictionEndpoint: "https://prediction.cmab.com/predict/%@" // Set your custom prediction endpoint template
)
        
// Initialize client with custom cmab config
let client = OptimizelyClient(
    sdkKey: SDK_KEY,
    cmabConfig: cmabConfig
)
📘

Note

Configure CMAB cache settings (size, TTL) and prediction endpoint at client initialization time. See Initialize the Swift SDK for details.

Source files

The language and platform source files containing the implementation for Swift are available on GitHub.