Optimizely will be sunsetting Full Stack Experimentation on July 29, 2024. See the recommended Feature Experimentation migration timeline and documentation.

Dev GuideAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunitySubmit a ticketLog In
GitHubNuGetDev CommunitySubmit a ticket

Migrate to Swift SDK

This guide describes how to migrate existing applications that use the Optimizely Full Stack Objective-C SDK to the Swift SDK.

Initialization

Compared to the Objective-C SDK, the Swift SDK simplifies the steps for SDK initialization for both synchronous and asynchronous modes.

Asynchronous initialization

For the Swift SDK, start with import optimizely for all platforms (instead of importing platform-specific framework names as in the Objective-C SDK, import OptimizelySDKiOS or import OptimizelySDKtvOS).

The hierarchy of OPLTYManager and OPTLYClient in the Objective-C SDK is a single layer of OptimizelyClient in the Swift SDK. The builder pattern for the Objective-C initialization has been removed as well.

As a result, the SDK module of the Swift SDK can be instantiated with a single instruction, Optimizely(sdkKey:), with a parameter of sdkKey.

After OptimizelyClient is instantiated, it should be explicitly started with a completion handler by calling OptimizelyClient.start(completionHandler:). If starting the SDK fails due to any error, OptimizelyClient will be set to the error state and terminate all subsequent API calls gracefully.

The following code examples show asynchronous initialization in the Swift SDK:

import optimizely

// Build and config OptimizelyClient
optimizely = OptimizelyClient(sdkKey: "SDK_KEY_HERE")
        
// Instantiate it asynchronously with a callback
optimizely.start { result in
    // initialization completed
}
@import optimizely;

// Build and config OptimizelyClient
self.optimizely = [[OptimizelyClient alloc] initWithSdkKey:@"SDK_KEY_HERE"];
    
// Or, instantiate it asynchronously with a callback
[self.optimizely startWithCompletion:^(NSData *data, NSError *error) {
    // initialization completed
}];

The following code examples show asynchronous initialization in the legacy Objective-C SDK:

import OptimizelySDKiOS

// Build a manager
let manager = OPTLYManager(builder: OPTLYManagerBuilder(block: { (builder) in
       builder?.sdkKey = "SDK_KEY_HERE"
    }))

// Instantiate it asynchronously with a callback
manager?.initialize(callback: { (error, optimizelyClient) in
   // initialization completed
})
#import <OptimizelySDKiOS/OptimizelySDKiOS.h>

// Build a manager
OPTLYManager *manager = [[OPTLYManager alloc] initWithBuilder:[OPTLYManagerBuilder  builderWithBlock:^(OPTLYManagerBuilder * _Nullable builder) {
        builder.sdkKey = @"SDK_KEY_HERE";
    }]];

// Instantiate it asynchronously with a callback
[manager initializeWithCallback:^(NSError * _Nullable error,
                                  OPTLYClient * _Nullable client) {
    // initialization completed
}];

Synchronous initialization

After the Swift SDK is instantiated, it can be started synchronously with a given project datafile by calling OptimizelyClient.start(datafile:). This step is similar to instantiation in the Objective-C SDK.

One important change is that the Swift SDK throws an error (OptimizelyError) when the SDK initialization fails for any reason. This allows applications to take proper actions according to error type. If starting the SDK fails due to any error, OptimizelyClient will be set to the error state and terminate all subsequent API calls gracefully.

The following code examples show synchronous initialization in the Swift SDK:

// Build and config OptimizelyClient
optimizely = OptimizelyClient(sdkKey: "SDK_KEY_HERE")
        
// Instantiate a client synchronously using a given datafile
do {
   try optimizely.start(datafile: datafileJSON)    
} catch {
   // errors
}
// Build and config OptimizelyClient
self.optimizely = [[OptimizelyClient alloc] initWithSdkKey:@"SDK_KEY_HERE"];
    
// Instantiate a client synchronously using a given datafile
BOOL status = [self.optimizely startWithDatafile:datafileJSON error:nil];

The following code examples show synchronous initialization in the legacy Objective-C SDK:

import OptimizelySDKiOS

// Build a manager
let manager = OPTLYManager(builder: OPTLYManagerBuilder(block: { (builder) in
            builder?.sdkKey = "SDK_KEY_HERE"
            Builder?.datafile = datafileJSON
        }))

// Instantiate a client synchronously using a given datafile
let optimizelyClient : OPTLYClient? = manager?.initialize()
#import <OptimizelySDKiOS/OptimizelySDKiOS.h>

// Build a manager
OPTLYManager *manager = [[OPTLYManager alloc] initWithBuilder:[OPTLYManagerBuilder  builderWithBlock:^(OPTLYManagerBuilder * _Nullable builder) {
        builder.sdkKey = @"SDK_KEY_HERE";
        Builder.datafile = datafileJSON;
    }]];

// Instantiate a client synchronously using a given datafile
OPTLYClient *client = [manager initialize];

Feature and experiment APIs

The Swift SDK provides the same set of APIs for features and experiments as the Objective-C SDK and provides the same functionality.

Some of the APIs are upgraded with Swift-native error handling support. When the SDK detects errors (such as invalid experiment keys or SDK not ready), the Swift SDK throws properly typed errors (OptimizelyError). This is different from the Objective-C SDK, which returns nil on error. In this way, the Swift SDK returns more information about detected errors so that applications can take proper action according to the error type. If specific error handling is not necessary, those APIs can be called with “try?” to suppress error throwing and then the Swift SDK will return nil for errors like the Objective-C SDK.

Activate

The Swift SDK throws an OptimizelyError when any error is detected while processing the activate call. When the API completes successfully, it returns the key value (string) of the selected variation (the Objective-C SDK returns an entire object of the selected variation).

Other than these changes, the Swift SDK functionality is same as that of the Objective-C SDK (except for a slight change in parameter labels).

Code examples for the Swift SDK:

let attributes = [
  "device": "iPhone",
  "lifetime": 24738388,
  "is_logged_in": true,
]

// Activate an A/B test
do {
  let variationKey = try optimizely.activate(experimentKey: "experiment", userId: "user_123", attributes: attributes) {
  if variationKey == "control" {
     // Execute code for "control" variation
  } else {
     // Execute code for other variation
  }
  
} catch {
   // Execute code for users who don't qualify for the experiment 
   // or for errors detected
}
NSDictionary *attributes = @{
  @"device": @"iPhone",
  @"lifetime": @24738388,
  @"is_logged_in": @true
};

// Activate an A/B test
NSString *variationKey = [optimizely activateWithExperimentKey:@"experiment"
                                  userId:@"user_123"
                              attributes:attributes
                                  error:nil];
...

Code examples for the legacy Objective-C SDK:

let attributes = [
  "device": "iPhone",
  "lifetime": 24738388,
  "is_logged_in": true,
]

// Activate an A/B test
if let variation = optimizelyClient.activate("experiment",
                           userId:"user_123",      
                           attributes:attributes){
   if variation.key == “control {
       // Execute code for "control" variation
   } else {
       // Execute code for other variation
   }
} else {
   // Execute code for users who don't qualify for the experiment or
   // for errors detected
}
NSDictionary *attributes = @{
  @"device": @"iPhone",
  @"lifetime": @24738388,
  @"is_logged_in": @true
};

// Activate an A/B test
OPTLYVariation *variation = [optimizelyClient activate:@"experiment", 
userId:@"user_123",
                                            attributes:attributes];

...

isFeatureEnabled

In the Swift SDK, the isFeatureEnabled API is identical to that of the Objective-C SDK (except for a slight change in parameter labels). Note that isFeatureEnabled returns false instead of throwing an error when errors are detected.

Code examples for the Swift SDK:

let attributes = [
  "device": "iPhone",
  "lifetime": 24738388,
  "is_logged_in": true,
]

// Evaluate a feature flag
let enabled = optimizely.isFeatureEnabled(featureKey: "feature", userId:"user_123", attributes:attributes)
NSDictionary *attributes = @{
  @"device": @"iPhone",
  @"lifetime": @24738388,
  @"is_logged_in": @true
};

// Evaluate a feature flag
BOOL enabled = [optimizely isFeatureEnabledWithFeatureKey:@"feature"
userId:@"user_123",
                                       attributes:attributes];

Code examples for the legacy Objective-C SDK:

let attributes = [
  "device": "iPhone",
  "lifetime": 24738388,
  "is_logged_in": true,
]

// Evaluate a feature flag
let enabled = optimizelyClient.isFeatureEnabled("feature", userId:"user_123", attributes:attributes)
NSDictionary *attributes = @{
  @"device": @"iPhone",
  @"lifetime": @24738388,
  @"is_logged_in": @true
};

// Evaluate a feature flag
bool enabled = [optimizelyClient isFeatureEnabled:@"feature",
                                           userId:@"user_123",
                                       attributes:attributes];

getFeatureVariable

When any error is detected and the value of the feature variable cannot be determined, the Swift SDK throws an OptimizelyError. Other than that, the getFeatureVariable functionality is same as in the Objective-C SDK (except for a slight change in parameter labels).

Code examples for the Swift SDK:

let attributes = [
  "device": "iPhone",
  "lifetime": 24738388,
  "is_logged_in": true,
]

do {

  let featureVariableValue = try optimizely.getFeatureVariableString(featureKey: "feature", variableKey: "variable", userId: "user_123", attributes:attributes)                                                                             
} catch {
   // error
}
NSDictionary *attributes = @{
  @"device": @"iPhone",
  @"lifetime": @24738388,
  @"is_logged_in": @true
};

NSString *featureVariableValue = [optimizely getFeatureVariableStringWithFeatureKey: @"variable"		variableKey:@"variable”					userId:@"user_123"
       attributes:attributes						error:nil];

Code examples for the legacy Objective-C SDK:

let attributes = [
  "device": "iPhone",
  "lifetime": 24738388,
  "is_logged_in": true,
]

let featureVariableValue = optimizelyClient.getFeatureVariableString("feature", variableKey:”variable", userId:"user_123", attributes:attributes)
NSDictionary *attributes = @{
  @"device": @"iPhone",
  @"lifetime": @24738388,
  @"is_logged_in": @true
};

NSString *featureVariableValue = [optimizelyClient getFeatureVariableString:@"feature",                                                           variableKey:@”variable",                                                                  userId:@"user_123",                                                              attributes:attributes];

track

The Swift SDK throws an OptimizelyError when any error is detected while processing event tracking. Other than that, the track functionality is same as in the Objective-C SDK (except for a slight change in parameter labels).

Code examples for the Swift SDK:

let attributes = [
  "device": "iPhone",
  "lifetime": 24738388,
  "is_logged_in": true,
]

let tags = [
  "category": "shoes",
  "count": 2,
]

// Track a conversion event for the provided user with attributes
try? optimizely.track(eventKey: "event", userId: "user_123", attributes: attributes, eventTags: eventTags)
NSDictionary *attributes = @{
  @"device": @"iPhone",
  @"lifetime": @24738388,
  @"is_logged_in": @true
};

NSDictionary *tags = @{
  @"category" : @"shoes",
  @"count": @5
};

// Track a conversion event for the provided user with attributes
[optimizely trackWithEventKey:@"event"
                               userId:@"user_123"
                           attributes:attributes
                            eventTags:tags
                                error:nil];

Code examples for the legacy Objective-C SDK:

let attributes = [
  "device": "iPhone",
  "lifetime": 24738388,
  "is_logged_in": true,
]

let tags = [
  "category": "shoes",
  "count": 2,
]

optimizelyClient.track("event", userId:"user_123", attributes:attributes, eventTags:tags);
NSDictionary *attributes = @{
  @"device": @"iPhone",
  @"lifetime": @24738388,
  @"is_logged_in": @true
};

NSDictionary *tags = @{
  @"category" : @"shoes",
  @"count": @5
};

[optimizelyClient track:@"event"
                 userId:@"user_123"
             attributes:attributes
              eventTags:tags];

Customization

Just like the Objective-C SDK, the Swift SDK allows you to customize key service modules including Logger, EventDispatcher, and UserProfileService. The Swift SDK does not include support for custom ErrorHandlers because errors are forwarded to applications via error throw.

Customization is optional. The Swift SDK implements full-featured default versions of these modules, serving the demands of most applications.

Custom modules are passed as optional parameters when the Swift SDK is initialized. When custom versions are not provided, the default modules are used automatically.

Logger

The Swift SDK logs all messages with os_log() by default, which writes messages to the device console with proper level controls and categories.

If necessary, logger functionality can be replaced with a custom version that complies with the OPTLogger protocol.

Code examples for the Swift SDK:

// create OptimizelyClient with a custom Logger
let customLogger = CustomLogger()

optimizely = OptimizelyClient(sdkKey: "SDK_KEY_HERE",
                              logger: customLogger)
// create OptimizelyClient with a custom Logger
CustomLogger *customLogger = [[CustomLogger alloc] init];
     
self.optimizely = [[OptimizelyClient alloc] initWithSdkKey:@"SDK_KEY_HERE"
                            				logger:customLogger
                            				eventDispatcher:nil
                            				userProfileService:nil
                            				periodicDownloadInterval:nil
                            				defaultLogLevel:OptimizelyLogLevelInfo];

Code examples for the legacy Objective-C SDK:

let customLogger = CustomLogger()

let manager = OPTLYManager(builder: OPTLYManagerBuilder(block: { (builder) in
  builder?.sdkKey = "SDK_KEY_HERE"
  builder?.logger = customLogger
}))
CustomLogger *customLogger = [[CustomLogger alloc] init];

OPTLYManager *manager = [[OPTLYManager alloc] initWithBuilder:[OPTLYManagerBuilder  builderWithBlock:^(OPTLYManagerBuilder * _Nullable builder) {
  builder.sdkKey = @"SDK_KEY_HERE";
  builder.logger = customLogger;
}]];

EventDispatcher

The Swift SDK implements a default version of a full-featured event dispatcher. All events are saved into permanent storage and removed only when they have been successfully delivered to the server. This process guarantees that events won’t be lost even when network connections fail or applications crash.

If necessary, the event dispatcher functionality can be replaced with a custom version that complies with the OPTEventDispatcher protocol.

Code examples for the Swift SDK:

// create OptimizelyClient with a custom EventDispatcher
let customEventDispatcher = CustomEventDispatcher()
      
optimizely = OptimizelyClient(sdkKey: sdkKey,
                              eventDispatcher: customEventDispatcher)
// create OptimizelyClient with a custom EventDispatcher
CustomEventDispatcher *customEventDispatcher = [[CustomEventDispatcher alloc] init];
     
self.optimizely = [[OptimizelyClient alloc] initWithSdkKey:kOptimizelySdkKey
                       			logger:nil
                       			eventDispatcher:customEventDispatcher
                       			userProfileService:nil
                         		periodicDownloadInterval:nil                       
                       			defaultLogLevel:OptimizelyLogLevelInfo];

Code examples for the legacy Objective-C SDK:

let customEventDispatcher = CustomEventDispatcher()

let manager = OPTLYManager(builder: OPTLYManagerBuilder(block: { (builder) in
  builder?.sdkKey = "SDK_KEY_HERE"
  builder?.eventDispatcher = customEventDispatcher
 }))
CustomEventDispatcher *customEventDispatcher = [[CustomEventDispatcher alloc] init];

OPTLYManager *manager = [[OPTLYManager alloc] initWithBuilder:[OPTLYManagerBuilder  builderWithBlock:^(OPTLYManagerBuilder * _Nullable builder) {
      builder.sdkKey = @"SDK_KEY_HERE";
      builder.eventDispatcher = customEventDispatcher;
    }]];

UserProfileService

The Swift SDK implements a default version of an UserProfileService, which saves experiment decisions for users in permanent storage and provides consistent results.

If necessary, the functionality can be replaced with a custom version complying to the OPTUserProfileService protocol.

Code examples for the Swift SDK:

// create OptimizelyClient with a custom UserProfileService
let customUserProfileService = CustomUserProfileService()

let optimizely = OptimizelyClient(sdkKey: "SDK_KEY_HERE",
                              userProfileService: customUserProfileService)
// create OptimizelyClient with a custom UserProfileService
CustomUserProfileService *customUserProfileService = [[CustomUserProfileService alloc] init];
     
self.optimizely = [[OptimizelyClient alloc] initWithSdkKey:@"SDK_KEY_HERE"
                            				logger:nil
                            				eventDispatcher:nil
                            				userProfileService:customUserProfileService
                            				periodicDownloadInterval:nil
                            				defaultLogLevel:OptimizelyLogLevelInfo];

Code examples for the legacy Objective-C SDK:

let customUserProfileService = CustomUserProfileService()

let manager = OPTLYManager(builder: OPTLYManagerBuilder(block: { (builder) in
  builder?.sdkKey = "SDK_KEY_HERE"
  builder?.eventDispatcher = customUserProfileService
 }))
CustomUserProfileService *customUserProfileService = [[CustomUserProfileService alloc] init];

OPTLYManager *manager = [[OPTLYManager alloc] initWithBuilder:[OPTLYManagerBuilder  builderWithBlock:^(OPTLYManagerBuilder * _Nullable builder) {
      builder.sdkKey = @"SDK_KEY_HERE";
      builder.userProfileService = customUserProfileService;
    }]];

Notification Listeners

The Swift SDK provides the same notification listener APIs as the Objective-C SDK (v3.1.0) except for slight changes in parameter labels, allowing straightforward migration of notification listeners.

Activate Notifications

The Activate Notifications API is deprecated. Use DecisionService Notifications instead.
`

DecisionService Notifications

The new APIs for DecisionService Notifications were recently added to the Objective-C SDK (v3.1.0). If applications already use the new APIs for the Objective-C SDK, migration to the Swift SDK is straightforward because it provides identical APIs (except for a slight change in parameter labels).

If applications use the earlier APIs for Activate Notifications, replace them with the DecisionService Notifications API.

Code examples for the Swift SDK:

// Add a notification listener
let notificationId = optimizely.notificationCenter.addDecisionNotificationListener(decisionListener: { (type, userId, attributes, decisionInfo) in
     // process data here
})  

// Remove a specific notification listener
optimizely.notificationCenter.removeNotificationListener(notificationId: notificationId!)

// Remove notification listeners of a certain type
optimizely.notificationCenter.clearNotificationListeners(type: .decision)

// Remove all notification listeners
optimizely.notificationCenter.clearAllNotificationListeners()
// Add a notification listener
NSNumber *notificationId = [self.optimizely.notificationCenter addDecisionNotificationListenerWithDecisionListener:^(NSString *type, NSString *userId, NSDictionary<NSString *,id> *attributes, NSDictionary<NSString *,id> *decisionInfo) {
     // process data here
}];

// Remove a specific listener
[optimizely.notificationCenter removeNotificationListenerWithNotificationId:notificationId];

// Remove all of one type of listener
[optimizely.notificationCenter clearNotificationListenersWithType:NotificationTypeDecision];

// Remove all notification listeners
[optimizely.notificationCenter clearAllNotificationListeners];

Code examples for the legacy Objective-C SDK:

// Add a notification listener
let notificationId = optimizely.notificationCenter?.addDecisionNotificationListener({ (type, userId, attributes, decisionInfo) in
    // process data here
})

// Remove a specific notification listener
optimizely.notificationCenter?.removeNotificationListener(UInt(notificationId!))

// Remove notification listeners of a certain type
optimizely.notificationCenter?.clearNotificationListeners(OPTLYNotificationType.decision)

// Remove all notification listeners
optimizely.notificationCenter?.clearAllNotificationListeners()
// Add a notification listener
NSInteger notificationId = [optimizely.notificationCenter addDecisionNotificationListener:^(NSString * _Nonnull type, NSString * _Nonnull userId, NSDictionary<NSString *,id> * _Nullable attributes, NSDictionary<NSString *,id> * _Nonnull decisionInfo) {
    // process data here
}];

// Remove a specific listener
[optimizely.notificationCenter removeNotificationListener:notificationId];

// Remove all of one type of listener
[optimizely.notificationCenter clearNotificationListeners:OPTLYNotificationTypeDecision];

// Remove all notification listeners
[optimizely.notificationCenter clearAllNotificationListeners];

Track Notifications

In the Swift SDK, the APIs for adding and removing a track notification listener are identical to those of the Objective-C SDK (except for a slight change in parameter labels).

Code examples for the Swift SDK:

let notificationId = optimizely.notificationCenter.addTrackNotificationListener(trackListener: { (eventKey, userId, attributes, eventTags, event) in
         // process data here
})

// see “DecisionService Notifications” for remove examples
NSNumber *notifId = [self.optimizely.notificationCenter addTrackNotificationListenerWithTrackListener:^(NSString *eventKey,
                                                                                                  NSString *userId,
                                                                                                  NSDictionary<NSString *,id> *attributes, NSDictionary<NSString *,id> *eventTags, NSDictionary<NSString *,id> *event) {
         // process data here  
}];

// see “DecisionService Notifications” for remove examples

Code examples for the legacy Objective-C SDK:

// Add a track notification listener
let notificationId = optimizely.notificationCenter?.addTrackNotificationListener({ (eventKey, userId, attributes, eventTags, event) in
         // process data here
})

// see “DecisionService Notifications” for remove examples
// Add a Track notification listener
NSInteger notificationId = [optimizely.notificationCenter addTrackNotificationListener:^(NSString * _Nonnull eventKey, NSString * _Nonnull userId, NSDictionary<NSString *,NSString *> * _Nonnull attributes, NSDictionary * _Nonnull eventTags, NSDictionary<NSString *,NSObject *> * _Nonnull event) {
         // process data here
}];

// see “DecisionService Notifications” for remove examples