The availability of features may depend on your plan type. Contact your Customer Success Manager if you have any questions.

Dev guideRecipesAPI Reference
Dev guideAPI ReferenceUser GuideLegal TermsGitHubDev CommunityOptimizely AcademySubmit a ticketLog In
Dev guide

Initialize the Swift SDK

How to initialize the Optimizely Feature Experimentation Swift SDK in your application.

Use the start method to initialize the Swift SDK and instantiate an instance of the Optimizely client class that exposes API methods like the Decide methods. Each client corresponds to the datafile representing the state of a project for a certain environment.

Version

4.0.0

Description

The constructor accepts a configuration object to configure Optimizely Feature Experimentation.

The Swift SDK provides a default implementation, but you may want to override the optional parameters for your production environments. For example, you can set up a logger to catch issues, an event dispatcher to manage network calls, and a User Profile Service to ensure sticky bucketing.

Parameters

The table below lists the required and optional parameters for the start method:

ParameterTypeDescription
sdkKey
required
StringSDK Key
logger
optional
OPTLoggerA custom logger
eventDispatcher
optional
OPTEventDispatcherA custom event handler
userProfileService
optional
OTPUserProfileServiceA custom user profile service
odpManager
optional
OdpManagerA custom Optimizely Data Platform (ODP) manager
periodicDownloadInterval
optional
IntA custom interval for periodic background datafile download in seconds (default = 10 * 60 secs)
defaultLogLevel
optional
OptimizelyLogLevelLog level (default = OptimizelyLogLevel.info)
defaultDecideOptions
optional
ArrayThis parameter sets default decide options applied to all the Decide calls made during the lifetime of the Optimizely client. Additionally, you can pass options to individual Decide methods (does not overrides defaults).

For details on decide options, see OptimizelyDecideOption.
settings
optional
OptimizelySdkSettingsYou can configure SDK settings such as the Real-Time Segments for Feature Experimentation methods with the OptimizelySDKSettings struct (see the following code).

The Swift SDK enables the Real-Time Segments for Feature Experimentation by default. But, the methods do nothing unless you have enabled and configured Real-Time Segments for Feature Experimentation.

The following settings are optionally configurable when the Swift SDK is initialized:

  • ODP SegmentsCache size.withODPSegmentCacheSize
    • Default – 100
    • Set to 0 to disable caching.
  • ODP SegmentsCache timeout (in seconds) – .withODPSegmentCacheTimeout
    • Default – 600 secs (10 minutes)
    • Set to 0 to disable timeout (never expires).
  • ODP enabledisableOdp
    • Default – false (enabled)
    • When disabled, the Swift SDK will disable ODP-related features. The Swift SDK still creates and manages VUID regardless of this flag and supports VUID-based decisions. See anonymous users.
    • The Swift SDK returns or logs an odpNotEnabled error when ODP is disabled and its features are requested.
// You must configure Real-Time Segments for Feature Experimentation.
public struct OptimizelySdkSettings {

    /// The maximum size of audience segments cache (default: 100) - cache is disabled if this is set to zero.
    let segmentsCacheSize: Int

    /// The timeout in seconds of audience segments cache (default: 600secs) - timeout is disabled if this is set to zero.
    let segmentsCacheTimeoutInSecs: Int

    /// The timeout in seconds of odp segment fetch (default: 10secs) - the OS default timeout is used if this is set to zero.
    let timeoutForSegmentFetchInSecs: Int

    /// The timeout in seconds of odp event dispatch (default: 10secs) - the OS default timeout is used if this is set to zero.
    let timeoutForOdpEventInSecs: Int

    /// ODP features are disabled if this is set to true (default: false).
    let disableOdp: Bool
}

See ODPManager.

Returns

❗️

Warning

We currently support a single concurrent instance per SDK key.

Instantiates an instance of the Optimizely class.

Examples

In the Swift SDK, you do not need to manage the datafile directly. The SDK includes a datafile manager that provides support for datafile polling to automatically update the datafile on a regular interval while the application is in the foreground.

To use it:

  1. Create a OptimizelyClient by supplying your SDK Key and optional configuration settings. To obtain your SDK key for a Feature Experimentation project, go to Settings > Environments.

  2. Choose whether to start the client synchronously or asynchronously and use the appropriate start method to instantiate a client.

// Build and config OptimizelyClient
optimizely = OptimizelyClient(sdkKey: "<Your_SDK_Key>")
let user = optimizely.createUserContext(userId: userId)
        
// Synchronously initialize the client, then make a decision for a flag rule (such as an A/B test)
do {
	try optimizely.start(datafile: datafileJSON)
  
  let decision = user.decide(key: "flagKey")
  let variationKey = decision.variationKey        
} catch {
  // errors
}  

// Or, instantiate it asynchronously with a callback
optimizely.start { result in
	let decision = user.decide(key: "flagKey")
  let variationKey = decision.variationKey   
}
// Build and config OptimizelyClient
self.optimizely = [[OptimizelyClient alloc] initWithSdkKey:@"<Your_SDK_Key>"];
self.user = [optimizely createUserContextWithUserId:userId attributes:attributes];    
// Synchronously initialize the client, then activate an experiment
BOOL status = [self.optimizely startWithDatafile:datafileJSON error:nil];

OptimizelyDecision *decision = [user decideWithKey:flagKey]; 
NSString *variationKey = decision.variationKey;

// Or, instantiate it asynchronously with a callback
[self.optimizely startWithCompletion:^(NSData *data, NSError *error) {
  OptimizelyDecision *decision = [user decideWithKey:flagKey]; 
  NSString *variationKey = decision.variationKey;
}];

See the Swift example: AppDelegate.swift or Objective-C example: AppDelegate.m.

Use synchronous or asynchronous initialization

You have two choices for when to instantiate the Optimizely client: synchronous and asynchronous. The behavioral difference between the two methods is whether your application pings the Optimizely Feature Experimentation servers for a new datafile during initialization. The functional difference between the two methods is whether your app prioritizes accuracy or speed.

🚧

Important

You much decide to initialize the Swift SDK either synchronously or asynchronously. You cannot use both initialization methods.

Synchronous initialization

The synchronous method prioritizes speed over accuracy. Instead of attempting to download a new datafile every time you initialize an Optimizely client, your app uses a local version of the datafile. This local datafile can be cached from a previous network request.

When you initialize a client synchronously, the Optimizely manager first searches for a cached datafile. If one is available, the manager uses it to complete the client initialization. If the manager cannot find a cached datafile, the manager searches for a bundled datafile. If the manager finds a bundled datafile, it uses the datafile to complete the client initialization. If the manager cancannot find a bundled datafile, the manager cannot initialize the client.

Synchronous intialiation flow diagram

To initialize an OptimizelyClient object synchronously, call optimizelyClient.start(datafile:datafile).

🚧

Important

The datafile you pass must have the same project ID that you provided when initializing the Optimizely manager.

Asynchronous initialization

The asynchronous method prioritizes accuracy over speed. During initialization, your app requests the newest datafile from the CDN servers. Requesting the newest datafile ensures that your app always uses the most current project settings, but it also means your app cannot instantiate a client until it downloads a new datafile, discovers the datafile has not changed, or until the request times out. This process takes time.

Initializing a client asynchronously executes like the synchronous initialization, except the manager first attempts to download the newest datafile. This network activity is what causes an asynchronous initialization to take longer to complete.

If the network request returns an error (such as when network connectivity is unreliable) or if the manager discovers that the cached datafile is identical to the newest datafile, the manager then uses the synchronous approach. If the manager discovers that the datafile has been updated and now differs from the cached datafile, the manager downloads the new datafile and uses it to initialize the client.

Asynchronous initialization flow diagram

To initialize an OptimizelyClient object asynchronously, call optimizelyClient.start(completion:) and pass it a callback function.

🚧

Important

The datafile you pass must have the same project ID that you provided when initializing the Optimizely manager.

Configure datafile polling

During its initialization, the manager attempts to pull the newest datafile from the CDN servers. The Optimizely manager can then periodically check for and pull a new datafile at the time interval you set during its initialization with a process called datafile polling.

The SDK disables datafile polling by default, so the client only checks for a new datafile during initialization. To enable polling, set a non-zero interval value for the number of seconds the manager waits between datafile polling attempts.

// Datafile update interval can be configured when the SDK is configured
// Background datafile update is disabled if periodicDownloadInterval is zero
optimizely = OptimizelyClient(sdkKey: "<Your_SDK_Key>",
                              periodicDownloadInterval: 5*60)
// every 5 minutes
// Datafile update interval can be configured when the SDK is configured
// Background datafile update is disabled if periodicDownloadInterval is zero
self.optimizely = [[OptimizelyClient alloc] initWithSdkKey:@"<Your_SDK_Key>"
                    logger:nil
                    eventDispatcher:nil
                    userProfileService:nil
                    periodicDownloadInterval:5*60
                    defaultLogLevel:OptimizelyLogLevelInfo];

Usage notes

  • The minimum polling interval is 2 minutes while the app is open. If you specify shorter polling intervals than the minimum, the SDK automatically resets the intervals to 2 minutes.
  • The Optimizely manager only checks for new datafiles when the SDK is active and the app is running on iOS/tvOS. You must configure background updates in iOS using the app delegate and calling the datafile manager download.
  • If you have datafile polling enabled, the Swift SDK automatically updates when a new datafile is detected to ensure that you are always working with the latest datafile. To be notified when the project has updated, register for a datafile change through the datafile change notification listener.
  • If you wish to have your datafile only update when the app comes to foreground, you can do the following:
    1. Set the periodicDownloadInterval = 0
    2. Call start when coming from the foreground:
func applicationWillEnterForeground(_ application: UIApplication) {
        optimizely.start { result in
            switch result {
            case .failure(let error):
                print("Optimizely SDK initialization failed: \(error)")
            case .success:
                print("Optimizely SDK initialized successfully!")
              	// retest your features or experiments?
            }
						
            self.startWithRootViewController()
        }
    }

Enable bundled datafiles

When your customer opens your app for the first time after installing or updating it, the manager attempts to pull the newest datafile from Optimizely's CDN. If your app cannot contact the servers, the manager can substitute a datafile that you included (bundled) when you created your app.

By bundling a datafile within your app, you ensure that the Optimizely manager can initialize without waiting for a response from the CDN. This lets you prevent poor network connectivity from causing your app to hang or crash while it loads.

Datafile bundling works with both synchronous and asynchronous initializations because reverting to a bundled datafile happens during the Optimizely manager's initialization, before a client is instantiated.

optimizely = OptimizelyClient(sdkKey: "<Your_SDK_Key>")

do {
	let localDatafilePath = Bundle.main.path(forResource: “datafileName”, ofType: "json")!
	let datafileJSON = try String(contentsOfFile: localDatafilePath, encoding: .utf8)
	try optimizely.start(datafile: datafileJSON)
	
	// Optimizely SDK initialized successfully
} catch {
	// Optimizely SDK initialization failed
}
self.optimizely = [[OptimizelyClient alloc] initWithSdkKey:@"<Your_SDK_Key>"];

NSString *filePath = [[NSBundle mainBundle] pathForResource:@“fileName” ofType:@"json"]
NSString *datafileJSON = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];

BOOL status = [self.optimizely startWithDatafile:datafileJSON error:nil];

Dispose of the client

For effective resource management with the Optimizely Swift SDK, you must properly close the Optimizely client instance when it is no longer needed. This is done by calling optimizelyClient.close().

The .close() method ensures that the processes and queues associated with the instance are properly released. This is essential for preventing memory leaks and ensuring that the application runs efficiently, especially in environments where resources are limited or in applications that create and dispose of many instances over their lifecycle. See the source on GitHub.

ODPManager

ODPManager contains all the logic supporting Real-Time Segments for Feature Experimentation-related features, including audience segments, ODP events, and VUID management.

The Swift SDK enables the Real-Time Segments for Feature Experimentation methods by default. But, the methods do nothing unless you have enabled and configured Real-Time Segments for Feature Experimentation.

If necessary, to disable Real-Time Segments for Feature Experimentation altogether, set disableOdp: true.

Customize ODPManager

ODPManager can be customized with the settings parameter when OptimizelyClient is instantiated.

let odpSettings = OptimizelySdkSettings(segmentsCacheSize: 1000,
                                        segmentsCacheTimeoutInSecs: 30*60,
                                        disableOdp: false)
        
let optimizely = OptimizelyClient(sdkKey: "<Your_SDK_Key>",
                                  settings: odpSettings)

More sample code

Synchronous and asynchronous Swift SDK initializations

// Here are sample codes for synchronous and asynchronous SDK initializations with multiple options

// [Synchronous]
        
// [S1] Synchronous initialization
//      1. SDK is initialized instantly with a cached (or bundled) datafile
//      2. A new datafile can be downloaded in background and cached after the SDK is initialized.
//         The cached datafile will be used only when the SDK re-starts in the next session.

optimizely = OptimizelyClient(sdkKey: "<Your_SDK_Key>")
try? optimizely.start(datafile: localDatafile)
user = optimizely.createUserContext(userId: userId)
let decision = user.decide(key: "flagKey")
let variationKey = decision.variationKey        
        
// [S2] Synchronous initialization
//      1. SDK is initialized instantly with a cached (or bundled) datafile
//      2. A new datafile can be downloaded in background and cached after the SDK is initialized.
//         The cached datafile is used immediately to update the SDK project config.
        
optimizely = OptimizelyClient(sdkKey: "<Your_SDK_Key>")
try? optimizely.start(datafile: localDatafile,
                              doUpdateConfigOnNewDatafile: true)
user = optimizely.createUserContext(userId: userId)
let decision = user.decide(key: flagKey)
let variationKey = decision.variationKey        
        
// [S3] Synchronous initialization
//      1. SDK is initialized instantly with a cached (or bundled) datafile
//      2. A new datafile can be downloaded in background and cached after the SDK is initialized.
//         The cached datafile is used immediately to update the SDK project config.
//      3. Polling datafile periodically.
//         The cached datafile is used immediately to update the SDK project config.
        
optimizely = OptimizelyClient(sdkKey: "<Your_SDK_Key>",
                                      periodicDownloadInterval: 60)
try? optimizely.start(datafile: localDatafile)
user = optimizely.createUserContext(userId: userId)
let decision = user.decide(key: flagKey)
let variationKey = decision.variationKey        
        
// [Asynchronous]
        
// [A1] Asynchronous initialization
//      1. A datafile is downloaded from the server and the SDK is initialized with the datafile

optimizely = OptimizelyClient(sdkKey: "<Your_SDK_Key>")
optimizely.start { result in
		user = optimizely.createUserContext(userId: userId)
		let decision = user.decide(key: flagKey)
		let variationKey = decision.variationKey        
}
        
// [A2] Asynchronous initialization
//      1. A datafile is downloaded from the server and the SDK is initialized with the datafile
//      2. Polling datafile periodically.
//         The cached datafile is used immediately to update the SDK project config.
        
optimizely = OptimizelyClient(sdkKey: "<Your_SDK_Key>",
                                      periodicDownloadInterval: 60)
optimizely.start { result in
		user = optimizely.createUserContext(userId: userId)
		let decision = user.decide(key: flagKey)
		let variationKey = decision.variationKey        
}

// [A3] Asynchronous initialization (aync-await)
//      1. A datafile is downloaded from the server and the SDK is initialized with the datafile
//      2. Polling datafile periodically.
//         The cached datafile is used immediately to update the SDK project config.
optimizely = OptimizelyClient(sdkKey: "<Your_SDK_Key>",
                              periodicDownloadInterval: 60)
if #available(iOS 13, *) {
  Task { [optimizely] in
        do {
          try await optimizely.start()
        } catch {
          print("Optimizely SDK initiliazation failed: \(error)")
        }
       }
}

Source files

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