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 CommunitySumbit a ticketLog In
GitHubNuGetDev CommunitySumbit a ticket

Configure a user profile service

This topic describes how to set up a custom User Profile Service or use the default for the Optimizely Objective-C SDK.

Use a User Profile Service to persist information about your users and ensure variation assignments are sticky. For example, if you are working on a backend website, you can create an implementation that reads and saves user profiles from a Redis or memcached store.

The Objective-C SDK defaults to a User Profile Service that stores this state directly on the device. See the Objective-C SDK User Profile Service.

Use manager.userProfileService.lookup to read a customer’s user profile.

If the User Profile Service doesn't bucket a user as you expect, then check whether other features are overriding the bucketing. For more information, see How bucketing works.

No op in Objective-C

You can overwrite the User Profile Service by setting the User Profile Service to OPTLYUserProfileServiceNoOp.init()​. Here is an example:

let optimizelyManager = OPTLYManager.init { 
    (builder) in builder !.projectId =
    "12345678" builder ?.userProfileService =
    OPTLYUserProfileServiceNoOp.init () 
}
 
optimizelyManager ?.initialize (callback : { 
	(err, client) in	// activate an experiment let variation = client?.activate("Test", userId: "user_test_1") NSLog((variation?.variationKey)!)
}

The bucketing will no longer be sticky for returning visitors: new traffic allocation changes will be applied for all visitors. The default user profile in iOS stores the bucketing decision for every visitor, so if the traffic allocation changes, this change is not applied to visitors who already have bucketing in storage.

Implement a User Profile Service

If you want to implement a custom User Profile Service rather than using the default provided by Objective-C SDK, then your User Profile Service should expose two functions with the following signatures:

  • lookup: Takes a user ID string and returns a user profile matching the schema below.
  • save: Takes a user profile and persists it.

If you want to use the User Profile Service purely for tracking purposes and not sticky bucketing, you can implement only the save method (always return nil from lookup).

The code example below shows the JSON schema for the user profile object.

Use experiment_bucket_map to override the default bucketing behavior and define an alternate experiment variation for a given user. For each experiment that you want to override, add an object to the map. Use the experiment ID as the key and include a variation_id property that specifies the desired variation. If there isn't an entry for an experiment, then the default bucketing behavior persists.

In the example below, ^[a-zA-Z0-9]+$ is the experiment ID.

{
  "title": "UserProfile",
  "type": "object",
  "properties": {
    "user_id": {"type": "string"},
    "experiment_bucket_map": {"type": "object",
                              "patternProperties": {
                                 "^[a-zA-Z0-9]+$": {"type": "object",
                                                    "properties": {"variation_id": {"type":"string"}},
                                                    "required": ["variation_id"]}
                               }
                             }
  },
  "required": ["user_id", "experiment_bucket_map"]
}

The SDK uses the User Profile Service you provide to override Optimizely's default bucketing behavior in cases when an experiment assignment has been saved.

The User Profile Service will persist variation assignments across app updates. However, the User Profile Service will not persist variation assignments across app re-installs.

When implementing your own User Profile Service, we recommend loading the user profiles into the User Profile Service on initialization and avoiding performing expensive, blocking lookups on the lookup function to minimize the performance impact of incorporating the service.

When implementing in a multi-server or stateless environment, we suggest using this interface with a backend like Cassandra or Redis. You can decide how long you want to keep your sticky bucketing around by configuring these services.