DevCycle iOS Client SDK
The iOS Client SDK for DevCycle! This SDK uses our Client SDK APIs to perform all user segmentation and bucketing for the SDK, providing fast response times using our globally distributed edge workers all around the world.
The SDK is available as a package on CocoaPods, Carthage and Swift Package Manager. It is also open source and can be viewed on GitHub.
Requirementsβ
This version of the DevCycle iOS Client SDK supports a minimum of iOS 12.
Installationβ
CocoaPodsβ
The SDK can be installed into your iOS project by adding the following to your cocoapod spec:
pod 'DevCycle'
Then, run pod install
.
Carthageβ
Include the following in your Cartfile
to integrate DevCycle as a dependency to your project:
github "DevCycleHQ/ios-client-sdk"
Then, run carthage update --use-xcframeworks
. Drag the built .xcframework bundles from Carthage/Build into the "Frameworks and Libraries" section of your applicationβs Xcode project.
Swift Package Managerβ
To use the library with Swift Package Manager, include it as a dependency in your Package.swift
file like so:
...
dependencies: [
.package(url: "https://github.com/DevCycleHQ/ios-client-sdk.git", .upToNextMinor("1.2.1")),
],
targets: [
.target(
name: "YOUR_TARGET",
dependencies: ["DevCycle"]
)
],
...
You can also add it through Xcode, i.e. File > Swift Packages > Add Package Dependency
, then enter the repository clone URL.
Support for macOSβ
info
Support for the macOS was introduced in version 1.8.0 of the DevCycle iOS Client SDK.
The DevCycle iOS SDK now supports macOS 10.13 or higher. To leverage the SDK on macOS, please use the DevCycle mobile environment key as shown in the examples below.
Usageβ
Initializing the SDKβ
We recommend initializing the SDK once in didFinishLaunchingWithOptions
of your AppDelegate
to receive features for as soon as possible, and to pass around the client instance around in your app.
Swiftβ
Using the builder pattern we can initialize the DevCycle SDK by providing the DVCUser and DevCycle mobile environment key:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
let user = try DVCUser.builder()
.userId("my-user1")
.build()
guard let dvcClient = try DVCClient.builder()
.environmentKey("<DEVCYCLE_MOBILE_ENVIRONMENT_KEY>")
.user(user)
.build(onInitialized: nil)
self.dvcClient = dvcClient
...
return true
}
The user object needs either a user_id
, or isAnonymous
set to true
for an anonymous user.
Objective-Cβ
For Objective-C we use a standard callback pattern to initialize the DevCycle SDK by providing the DVCUser and DevCycle mobile environment key:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
DVCUser *user = [DVCUser initializeWithUserId:@"my-user1"];
self.dvcClient = [DVCClient initialize:@"<DEVCYCLE_MOBILE_ENVIRONMENT_KEY>"
user:user
options:nil
onInitialized:^(NSError * _Nullable error) {
if (error) {
NSLog(@"DevCycle failed to initialize: %@", error);
}
}];
...
return YES;
}
DVC Client Builderβ
The DVCClient can be built using the following methods:
Method | Parameter | Description |
---|---|---|
environmentKey | String | DevCycle environment key |
user | DVCUser | DevCycle user object |
options | DVCOptions | DevCycle options object |
DVC User Builderβ
The DVC user can be built using the following methods:
Method | Parameter | Description |
---|---|---|
userId | String | Unique user ID |
isAnonymous | Bool | Boolean to indicate if the user is anonymous |
String | User's email | |
name | String | User's name |
language | String | User's language |
country | String | User's country |
customData | [String: Any] | Key/value map of properties to be used for targeting |
privateCustomData | [String: Any] | Key/value map of properties to be used for targeting. Private properties will not be included in event logging. |
DVC Options Builderβ
The SDK exposes various initialization options which can be used by passing a DVCOptions
object to the withOptions
method of DVCClient.builder()
:
Method | Parameter | Default | Description |
---|---|---|---|
flushEventsIntervalMs | Int | 10000 | Controls the interval between flushing events to the DevCycle servers in milliseconds, defaults to 10 seconds. |
disableEventLogging | Bool | false | Disables logging of SDK generated events (e.g. aggVariableEvaluated, aggVariableDefaulted) to DevCycle. |
logLevel | LogLevel | error | Set log level of the default logger. Defaults to error |
enableEdgeDB | Boolean | false | Enables the usage of EdgeDB for DevCycle that syncs User Data to DevCycle. |
configCacheTTL | Int | 604800000 | The maximum allowed age of a cached config in milliseconds, defaults to 7 days |
disableConfigCache | Bool | false | Disable the use of cached configs |
Notifying when DevCycle features are availableβ
In the initialize call there is an optional onInitialized
parameter you can use to determine when your features have been loaded:
Swiftβ
self.dvcClient = try? DVCClient.builder()
.environmentKey("<DEVCYCLE_MOBILE_ENVIRONMENT_KEY>")
.user(user)
.options(options)
.build(onInitialized: { error in
if (error != nil) {
// there was an error with building the client
} else {
// initialized successfully
}
})
Objective-Cβ
self.dvcClient = [DVCClient initialize:@"<DEVCYCLE_MOBILE_ENVIRONMENT_KEY>"
user:user
options:nil
onInitialized:^(NSError * _Nullable error) {
if (error) {
NSLog(@"DevCycle failed to initialize: %@", error);
} else {
// initialized successfully
}
}];
Using Variable Valuesβ
To get values from your Features, the variable()
method is used to fetch variable values using
the variable's identifier key
coupled with a default value. The default value can be of type
string, boolean, number, or JSONObject:
Swiftβ
let boolVariable = dvcClient.variable(key: "bool_key", defaultValue: false)
let strVariable = dvcClient.variable(key: "string_key", defaultValue: "default")
let numVariable = dvcClient.variable(key: "num_key", defaultValue: 4)
let jsonVariable = dvcClient.variable(key: "json_key", defaultValue: [:])
Objective-Cβ
DVCVariable *boolVariable = [self.dvcClient boolVariableWithKey:@"bool_key" defaultValue:false];
DVCVariable *strVariable = [self.dvcClient stringVariableWithKey:@"string_key" defaultValue:@"default"];
DVCVariable *numVariable = [self.dvcClient numberVariableWithKey:@"num_key" defaultValue:@4];
DVCVariable *jsonVariable = [self.dvcClient jsonVariableWithKey:@"json_key" defaultValue:@{}];
To grab the value, there is a property on the object returned to grab the value:
Swiftβ
if (boolVariable.value == true) {
// Run Feature Flag Code
} else {
// Run Default Code
}
Objective-Cβ
if (boolVariable.value == true) {
// Run Feature Flag Code
} else {
// Run Default Code
}
The Variable
object also contains the following params:
- `key`: the key indentifier for the Variable
- `type`: the type of the Variable, one of: `String` / `Boolean` / `Number` / `JSON`
- `value`: the Variable's value
- `defaultValue`: the Variable's default value
- `isDefaulted`: if the Variable is using the `defaultValue`
- `evalReason`: evaluation reason for why the variable was bucketed into its value
If the value is not ready, it will return the default value passed in the creation of the variable.
Variable Updatesβ
Variable values update whenever identifyUser()
or resetUser()
are called, or when the project configuration changes (to learn more, visit our Realtime Updates page).
To listen for variable updates, the onUpdate()
method can be used. Please note, a strong reference to the variable is needed for onUpdate
to be triggered.
Swiftβ
let boolVariable = dvcClient.variable(key: "bool_key", defaultValue: false)
.onUpdate { value in
// Variable value updated
}
Objective-Cβ
DVCVariable *boolVar = [[self.dvcClient boolVariableWithKey:@"bool_key" defaultValue:true]
onUpdateWithHandler:^(id _Nonnull value) {
// Variable value updated
}];
Grabbing All Features / Variablesβ
Get All Featuresβ
To get all the Features returned in the config:
Swift
let features: [String: Feature] = dvcClient.allFeatures()
Objective C
NSDictionary *allFeatures = [self.dvcClient allFeatures];
If the SDK has not finished initializing, these methods will return an empty object.
Get All Variablesβ
To get all the variables returned in the config:
Swift
let variables: [String: Variable] = dvcClient.allVariables()
Objective-C
NSDictionary *allVariables = [self.dvcClient allVariables];
If the SDK has not finished initializing, these methods will return an empty object.
Identifying Userβ
To identify a different user, or the same user passed into the initialize method with more attributes,
build a DVCUser object and pass it into identifyUser
:
Swiftβ
do {
let user = try DVCUser.builder()
.userId("my-user1")
.email("[email protected]")
.country("CA")
.name("My Name")
.language("EN")
.customData([ "customkey": "customValue" ])
.privateCustomData([ "customkey2": "customValue2" ])
.build()
try dvcClient.identifyUser(user: user)
} catch {
print("Error building new DVCUser: \(error)")
}
Objective-Cβ
DVCUser *user = [DVCUser initializeWithUserId:@"my-user1"];
user.email = @"[email protected]";
user.appBuild = @1005;
user.appVersion = @"1.1.1";
user.country = @"CA";
user.name = @"My Name";
user.language = @"EN";
user.customData = @{@"customKey": @"customValue"};
user.privateCustomData = @{@"customkey2": @"customValue2"};
[self.dvcClient identifyUser:user callback:^(NSError *error, NSDictionary<NSString *,id> *variables) {
if (error) {
return NSLog(@"Error calling DVCClient identifyUser:callback: %@", error);
}
}];
To wait on Variables that will be returned from the identify call, you can pass in a DVCCallback:
Swiftβ
try dvcClient.identifyUser(user: user) { error, variables in
if (error != nil) {
// error identifying user
} else {
// use variables
}
}
Objective-Cβ
[self.dvcClient identifyUser:user callback:^(NSError *error, NSDictionary<NSString *,id> *variables) {
if (error) {
// error identifying user
} else {
// use variables
}
}];
If error
exists the called the user's configuration will not be updated and previous user's data will persist.
Reset Userβ
To reset the user into an anonymous user, resetUser
will reset to the anonymous user created before
or will create one with an anonymous user_id
.
Swiftβ
try dvcClient.resetUser()
Objective-Cβ
[self.dvcClient resetUser:nil];
To wait on the Features of the anonymous user, you can pass in a DVCCallback:
Swiftβ
try dvcClient.resetUser { error, variables in
// anonymous user
}
Objective-Cβ
[self.dvcClient resetUser:^(NSError *error, NSDictionary<NSString *,id> *variables) {
if (error) {
// Error resetting user, existing user used
} else {
// anonymous user
}
}];
If error
exists is called the user's configuration will not be updated and previous user's data will persist.
Tracking Eventsβ
To track events, pass in an object with at least a type
key:
Swiftβ
let event = try DVCEvent.builder()
.type("my_event")
.target("my_target")
.value(3)
.metaData([ "key": "value" ])
.clientDate(Date())
.build()
dvcClient.track(event)
Objective-Cβ
NSError *err = nil;
DVCEvent *event = [DVCEvent initializeWithType:@"my-event"];
[self.dvcClient track:event err:&err];
if (err) {
NSLog(@"Error calling DVCClient track:err: %@", err);
}
The SDK will flush events every 10s or flushEventsMS
specified in the options. To manually flush events, call:
Swiftβ
dvcClient.flushEvents()
Objective-Cβ
[self.dvcClient flushEvents];
EdgeDBβ
EdgeDB allows you to save user data to our EdgeDB storage so that you don't have to pass in all the user data every time you identify a user. Read more about EdgeDB.
To get started, contact us at [email protected] to enable EdgeDB for your project.
Once you have EdgeDB enabled in your project, pass in the enableEdgeDB option to turn on EdgeDB mode for the SDK:
Swiftβ
let user = try? DVCUser.builder()
.userId("test-user")
.customData([ "amountSpent": 50 ])
.build()
let options = DVCOptions.builder()
.enableEdgeDB(true)
.build()
Objective-Cβ
DVCUser *user = [DVCUser initializeWithUserId:@"test-user"];
user.customData = @{
@"amountSpent": @50,
};
DVCOptions *options = [[DVCOptions alloc] init];
options.enableEdgeDB = @true;
This will send a request to our EdgeDB API to save the custom data under the user test-user
.
In the example, amountSpent
is associated to the user test-user
. In your next identify call for the same userId
, you may omit any of the data you've sent already as it will be pulled from the EdgeDB storage when segmenting to experiments and features.