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
Updated almost 3 years ago