Initialize SDK
This topic describes how to initialize the Optimizely Objective-C SDK in your application.
In our Objective-C SDK, you do not need to manage the datafile directly. The SDK has an additional abstraction called a manager that handles getting the datafile and instantiating the client for you during the SDK's initialization process. The manager includes:
- A built-in User Profile Service for storing variation assignments on the device.
- A built-in event dispatcher for sending events in the background. It also retries sending using exponential backup if it fails.
- Support for datafile polling to automatically update the datafile on a regular interval while the application is in the foreground.
The manager is implemented as a factory for Optimizely client instances. To use it:
- Create a manager object by supplying your SDK Key and optional configuration settings for datafile polling and datafile bundling. You can find the SDK key in the Settings > Environments tab of a Full Stack project on the Optimizely application.
Note
Earlier versions of the SDK used a project ID rather than an SDK Key to create a manager. Project IDs are still supported in 2.x for backwards compatibility. If you supply a project ID, the SDK retrieves the primary environment's datafile. We recommend using SDK keys because they enable you to retrieve datafiles for other environments.
- Choose whether to instantiate the client synchronously or asynchronously and use the appropriate
initialize
method to instantiate a client.
// In AppDelegate.m
#import <OptimizelySDKiOS/OptimizelySDKiOS.h>
// Build a manager
OPTLYManager *manager = [[OPTLYManager alloc] initWithBuilder:[OPTLYManagerBuilder builderWithBlock:^(OPTLYManagerBuilder * _Nullable builder) {
builder.sdkKey = @"SDK_KEY_HERE";
}]];
// Instantiate a client synchronously using cached datafile
OPTLYClient *client = [manager initialize];
OPTLYVariation *variation = [client activate:@"experimentKey"
userId:@"userId"
attributes:nil];
// Or, instantiate it asynchronously with a callback
[manager initializeWithCallback:^(NSError * _Nullable error,
OPTLYClient * _Nullable client) {
OPTLYVariation *variation = [client activate:@"experimentKey"
userId:@"userId"
attributes:nil];
}];
// In AppDelegate.swift
import OptimizelySDKiOS
// Build a manager
let optimizelyManager = OPTLYManager(builder: OPTLYManagerBuilder(block: { (builder) in
builder?.sdkKey = "SDK_KEY_HERE"
}))
// Synchronously initialize the client, then activate an experiment
let optimizelyClient : OPTLYClient? = optimizelyManager?.initialize(withDatafile:jsonDatafile!)
let variation = optimizelyClient?.activate("experimentKey",
userId:"userId",
attributes:nil)
// Or, instantiate it asynchronously with a callback
optimizelyManager?.initialize(callback: { [weak self] (error, optimizelyClient) in
let variation = optimizelyClient?.activate("experimentKey",
userId:"userId",
attributes:nil)
})
For full examples, see:
Get a Client
Each manager retains a reference to its most recently initialized client. You can use the getOptimizely
function to return that instance.
If this method is called before any client objects have been initialized, a dummy client instance is created and returned. This dummy instance logs errors when any of its methods are used.
let optimizelyClient = [manager getOptimizely];
let optimizelyClient = optimizelyManager?.getOptimizely();
Note
The 3.0.x mobile SDK documentation uses the term
OptimizelyManager
. To be consistent with other Full Stack SDKs, this has been changed to theOptimizelyClient
in the Objective-C SDK.
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 servers for a new datafile during initialization. The functional difference between the two methods is whether your app prioritizes speed (synchronous method) or accuracy (asynchronous method).
Important
You much decide to initialize the Objective-C SDK either synchronously or asynchronously. You cannot use both initialization methods.
Synchronous initialization
The synchronous start does not access the CDN at startup time. By default, the SDK will attempt to update the cache in the background. 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 SDK first searches for a cached datafile. If one is available, the SDK uses it to complete the client initialization. If the SDK cannot find a cached datafile, the SDK searches for a bundled datafile. If the SDK finds a the bundled datafile, it uses the datafile to complete the client initialization. If the SDK can't find a bundled datafile, the SDK cannot initialize the client.
Asynchronous initialization
The asynchronous method always attempts to get a new version from the CDN. You can pass in a timeout interval; see the Apple documentation for urlsessionconfiguration.timeoutIntervalForResource
. 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 has the extra step of checking the CDN.
Initializing a client asynchronously executes like the synchronous initialization, except the SDK will first attempt to download the newest datafile.
If the network request returns an error (such as when network connectivity is unreliable) or if the SDK discovers that the cached datafile is identical to the newest datafile, the SDK then uses the synchronous approach. If the SDK discovers that the datafile has been updated and now differs from the cached datafile, the SDK downloads the new datafile and uses it to initialize the client.
Configure datafile polling
To initialize an OPTLYClient
object synchronously, call [manager initialize]
or [manager initializeWithDatafile:datafile]
.
To initialize an OPTLYClient
object asynchronously, call [manager initializeWithCallback:callback]
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.
During its initialization, the manager attempts to pull the newest datafile from the CDN servers. After this, the Optimizely manager can periodically check for and pull a new datafile at the time interval you set during its initialization. The process of checking for and downloading a new datafile is called datafile polling.
By default, the manager only checks for a new datafile during initialization. To enable periodic datafile polling, you must define a polling interval when building the manager. This value is the number of seconds the manager waits between datafile polling attempts.
OPTLYDatafileManagerDefault *datafileManager = [[OPTLYDatafileManagerDefault alloc] initWithBuilder:[OPTLYDatafileManagerBuilder builderWithBlock:^(OPTLYDatafileManagerBuilder * _Nullable builder) {
builder.datafileConfig = [[OPTLYDatafileConfig alloc] initWithProjectId:nil withSDKKey:@"SDK_KEY_HERE"];
builder.datafileFetchInterval = 120.0;
}]];
// Build a manager
OPTLYManager *manager = [[OPTLYManager alloc] initWithBuilder:[OPTLYManagerBuilder builderWithBlock:^(OPTLYManagerBuilder * _Nullable builder) {
builder.sdkKey = @"SDK_KEY_HERE";
builder.datafileManager = datafileManager;
}]];
// ---- Create the Datafile Manager ----
let datafileManager = OPTLYDatafileManagerDefault(builder: OPTLYDatafileManagerBuilder(block: { (builder) in
builder!.datafileFetchInterval = TimeInterval(self.datafileManagerDownloadInterval)
builder!.datafileConfig = OPTLYDatafileConfig(projectId: nil, withSDKKey:"SDK_KEY_HERE")!;
}))
let builder = OPTLYManagerBuilder(block: { (builder) in
builder!.projectId = nil;
builder!.sdkKey = "SDK_KEY_HERE"
builder!.datafileManager = datafileManager!
builder!.eventDispatcher = eventDispatcher
})
// ---- Create the Manager ----
var optimizelyManager = OPTLYManager(builder: builder)
Usage notes
-
In the Objective-C SDK, the minimum polling interval is 2 minutes while the app is open. If you specify a shorter polling interval, the SDK will automatically reset the intervals to 2 minutes.
-
Updated datafiles do not take effect until your app is restarted or when you re-initialize the Optimizely manager. This implementation strategy allows the data to change while the app is running without causing nondeterministic behavior.
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.
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 is unable to 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 initialization because reverting to a bundled datafile happens during the Optimizely manager's initialization, before a client is instantiated.
OPTLYManager *manager = [[OPTLYManager alloc] initWithBuilder:[OPTLYManagerBuilder builderWithBlock:^(OPTLYManagerBuilder * _Nullable builder) {
// Load the datafile from the bundle
NSString *filePath =[[NSBundle bundleForClass:[self class]]
pathForResource:@"datafileName"
ofType:@"json"];
NSString *fileContents =[NSString stringWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding
error:nil];
NSData *jsonData = [fileContents dataUsingEncoding:NSUTF8StringEncoding];
// Set the datafile in the builder
builder.datafile = jsonData;
builder.sdkKey = @"SDK_KEY_HERE";
}]];
let manager = OPTLYManager(builder: OPTLYManagerBuilder(block: { (builder) in
builder?.sdkKey = "SDK_KEY_HERE"
// Load the datafile from the bundle
let bundle = Bundle.init(for: self.classForCoder)
let filePath = bundle.path(forResource: "datafileName", ofType: "json")
var jsonDatafile: Data? = nil
do {
jsonDatafile = try Data(contentsOf: URL(fileURLWithPath: filePath!), options: .mappedIfSafe)
}
catch {
print("Invalid JSON data")
}
// Set the datafile in the builder
builder!.datafile = jsonDatafile
}))
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 is unable to 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 initialization because reverting to a bundled datafile happens during the Optimizely manager's initialization, before a client is instantiated.
OPTLYManager *manager = [[OPTLYManager alloc] initWithBuilder:[OPTLYManagerBuilder builderWithBlock:^(OPTLYManagerBuilder * _Nullable builder) {
// Load the datafile from the bundle
NSString *filePath =[[NSBundle bundleForClass:[self class]]
pathForResource:@"datafileName"
ofType:@"json"];
NSString *fileContents =[NSString stringWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding
error:nil];
NSData *jsonData = [fileContents dataUsingEncoding:NSUTF8StringEncoding];
// Set the datafile in the builder
builder.datafile = jsonData;
builder.sdkKey = @"SDK_KEY_HERE";
}]];
let manager = OPTLYManager(builder: OPTLYManagerBuilder(block: { (builder) in
builder?.sdkKey = "SDK_KEY_HERE"
// Load the datafile from the bundle
let bundle = Bundle.init(for: self.classForCoder)
let filePath = bundle.path(forResource: "datafileName", ofType: "json")
var jsonDatafile: Data? = nil
do {
jsonDatafile = try Data(contentsOf: URL(fileURLWithPath: filePath!), options: .mappedIfSafe)
}
catch {
print("Invalid JSON data")
}
// Set the datafile in the builder
builder!.datafile = jsonDatafile
}))
Updated about 1 year ago