iOS
This category contains topics explaining how to configure Bucketeer's iOS SDK.
Bucketeer iOS SDK has reached the production stage, offering you a stable and reliable experience.
The Bucketeer iOS SDK is compatible with iOS versions 11.0 and higher.
Getting started
Before starting, ensure that you follow the Getting Started guide.
Implementing dependency
To find the latest version, refer to the SDK releases page.
- Cocoapods
- Swift Package Manager
- Carthage
Implement the SDK in your Podfile file.
use_frameworks!
target 'YOUR_TARGET_NAME' do
pod 'Bucketeer', 'LATEST_VERSION'
end
Implement the SDK as a dependency in your Package.swift file or through Xcode.
// Package.swift
dependencies: [
.package(url: "https://github.com/bucketeer-io/ios-client-sdk.git", exact: "LATEST_VERSION"),
],
targets: [
.target(
name: "YOUR_TARGET_NAME",
dependencies: [.product(name: "Bucketeer", package: "ios-client-sdk")],
)
],
To include a package dependency in your Xcode project, follow these steps:
Go to File -> Swift Packages -> Add Package Dependency. Next, enter the clone URL of the iOS SDK repository, and specify the version you desire.
Implement the SDK in your Cartfile file.
github "bucketeer-io/ios-client-sdk" ~> LATEST_VERSION
Importing client
Import the Bucketeer client into your application code.
- Swift
import Bucketeer
Configuring client
Configure the SDK config and user configuration.
The featureTag setting is the tag you configure when creating a Feature Flag. It will evaluate all the Feature Flags in the environment when it is not configured.
We strongly recommend using tags to speed up the evaluation process and reduce the cache size in the client.
All the settings in the example below are required.
- Swift
do {
// SDK configuration
let config = try BKTConfig.Builder()
.with(apiKey: "YOUR_API_KEY")
.with(apiEndpoint: "YOUR_API_ENDPOINT")
.with(featureTag: "YOUR_FEATURE_TAG")
.with(appVersion: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String)
.build()
// User configuration
let user = try BKTUser.Builder()
.with(id: "USER_ID")
.with(attributes: [:]) // The user attributes are optional
.build()
} catch {
// Error handling
}
Depending on your use, you may want to change the optional configurations available in the BKTConfig.
- pollingInterval - Minimum 60 seconds. Default is 10 minutes (In Milliseconds)
- backgroundPollingInterval - Minimum 20 minutes. Default is 1 hour (In Milliseconds)
- eventsFlushInterval - Minimum 60 seconds. Default is 60 seconds (In Milliseconds)
- eventsMaxQueueSize - Default is 50 events
The Bucketeer SDK doesn't save the user data. The Application must save and set it when initializing the client SDK.
Initializing client
Initialize the client by passing the configurations in the previous step.
- Swift
BKTClient.initialize(config: config, user: user)
The initialize process default timeout is 5 seconds.
Once initialization is finished, all the requests in the SDK use a timeout of 30 seconds.
If you want to use the feature flag on Splash or Main views, the SDK cache may be old or not exist and may not have enough time to fetch the variations from the Bucketeer server. In this case, we recommend using the callback in the initialize method. In addition, you can define a custom timeout.
During the initialization process, errors are not related to the initialization itself. Instead, they arise from a timeout request, indicating the variations data from the server weren't received. Therefore, the SDK will work as usual and update the variations in the next polling request.
The completion callback is called on the main thread.
- Swift
let timeout: Int64 = 2000 // Default is 5 seconds (In milliseconds)
BKTClient.initialize(
config: config,
user: user,
timeoutMillis: timeout
) { error in
// The completion callback is called on the main thread
guard error == nil else {
// Handle the error when there is no cache or the cache is not updated
return
}
let client = BKTClient.shared
let showNewFeature = client.boolVariation(featureId: "YOUR_FEATURE_FLAG_ID", defaultValue: false)
if (showNewFeature) {
// The Application code to show the new feature
} else {
// The code to run when the feature is off
}
}
Polling
The initialize process immediately starts polling the latest evaluations from the Bucketeer server in the background using the interval pollingInterval
configuration while the application is in the foreground state. When the application changes to the background state, it will use the backgroundPollingInterval
configuration when the Background fetch is configured.
Polling retry behavior
The Bucketeer SDK regularly polls the latest evaluations from the server based on the pollingInterval
parameter. By default, the pollingInterval
is set to 10 minutes, but you can adjust it to suit your needs.
If a polling request fails, the SDK initiates a retry procedure. The SDK attempts a new polling request every minute up to 5 times. If all five retry attempts fail, the SDK sends a new polling request once the pollingInterval
time elapses. The table below shows this scenario:
Polling Time | Retry Time | Request Status |
---|---|---|
10:00 | - | Fail |
- | 10:01 | Fail |
- | 10:02 | Fail |
- | 10:03 | Fail |
- | 10:04 | Fail |
- | 10:05 | Fail |
10:10 | - | Successful |
The polling counter, which uses the pollingInterval
information, resets in case of a successful retry. The table below shows the described scenario.
Polling Time | Retry Time | Request status |
---|---|---|
10:00 | - | Fail |
- | 10:01 | Successful |
10:11 | - | Successful |
Supported features
Evaluating user
The variation method determines whether or not a feature flag is enabled for a specific user.
To check which variation a specific user will receive, you can use the client like below.
- Swift
let client = BKTClient.shared
let showNewFeature = client.boolVariation(featureId: "YOUR_FEATURE_FLAG_ID", defaultValue: false)
if (showNewFeature) {
// The Application code to show the new feature
} else {
// The code to run when the feature is off
}
The variation method will return the default value if the feature flag is missing in the SDK.
Variation types
The Bucketeer SDK supports the following variation types.
- Swift
func boolVariation(featureId: String, defaultValue: Bool) -> Bool
func stringVariation(featureId: String, defaultValue: String) -> String
func intVariation(featureId: String, defaultValue: Int) -> Int
func doubleVariation(featureId: String, defaultValue: Double) -> Double
func jsonVariation(featureId: String, defaultValue: [String: AnyHashable]) -> [String: AnyHashable]
Updating user evaluations
Depending on the use case, you may need to ensure the evaluations in the SDK are up to date before requesting the variation.
The fetch method uses the following parameters.
- Completion callback - The callback is returned on the main thread
- Timeout - Default is 30 seconds (In milliseconds)
- Swift
let timeout: Int64 = 5000 // Optional. Default is 30 seconds (In milliseconds)
let client = BKTClient.shared
client.fetchEvaluations(timeoutMillis: timeout) { error in
// The completion callback is called on the main thread
guard error == nil else {
// The code to run when there is an error while initializing the SDK
return
}
let showNewFeature = client.boolVariation(featureId: "YOUR_FEATURE_FLAG_ID", defaultValue: false)
if (showNewFeature) {
// The Application code to show the new feature
} else {
// The code to run when the feature is off
}
}
Depending on the client network, it could take a few seconds until the SDK fetches the data from the server, so use this carefully.
You don't need to call this method manually in regular use because the SDK is polling the latest variations in the background.
Updating user evaluations in real-time
Bucketeer SDK supports FCM (Firebase Cloud Messaging).
Every time you change a feature flag on the admin console, Bucketeer will send a silent notification using the FCM V1 API to notify the client so that you can update the evaluations in real-time.
- You need to register your FCM Service Account on the admin console. Check the Pushes section to learn how to do it.
- This feature may not work if the end-user has the notification disabled.
When using the FCM integration, your applications must subscribe to Bucketeer's topic, so they can receive notifications.
The topic varies depending on the feature flag tag.
E.g.: bucketeer-<YOUR_FEATURE_FLAG_TAG>
The tag in the topic is the same tag used when initializing the client SDK.
If you have a flag using the ios
tag, the topic will be bucketeer-ios.
Please be aware that the tag is case-sensitive.
Assuming you already have the FCM implementation in your application, the following example shows how to handle silent push notifications.
- Swift
```swift showLineNumbers
// In order to receive notifications you must subscribe to the topic
func subscribeToTopic() {
let tag = "ios" // The same tag used when initializing the client SDK
let topic = "bucketeer-\(tag)"
Messaging.messaging().subscribe(toTopic: topic) { error in
if let error = error {
print("Failed to subscribed to \(topic) topic. Error: \(error)")
} else {
print("Subscribed successfully to \(topic) topic")
}
}
}
// Receiving notification in background
func application(
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any]
) async -> UIBackgroundFetchResult {
let flag = userInfo["bucketeer_feature_flag_updated"] as? String
if flag == "true" {
let client = BKTClient.shared
let showNewFeature = client.boolVariation(featureId: "YOUR_FEATURE_FLAG_ID", defaultValue: false)
if (showNewFeature) {
// The Application code to show the new feature
} else {
// The code to run when the feature is off
}
}
return UIBackgroundFetchResult.newData
}
Reporting custom events
This method lets you save user actions in your application as events. You can connect these events to metrics in the experiments console UI.
In addition, you can pass a double value to the goal event. These values will sum and show as Value total
on the experiments console UI. This is useful if you have a goal event for tracking how much a user spent on your application buying items, etc.
The default track value is 0.0.
- Swift
client.track(goalId: "YOUR_GOAL_ID", value: 10.50)
Flushing events
This method will send all pending analytics events to the Bucketeer server immediately. This process is asynchronous, so it returns before it is complete.
If you need to check the result, you can use the completion callback, returned on the main thread.
- Swift
client.flush { error in
// The completion callback is called on the main thread
guard error == nil else {
// Error handling
return
}
}
In regular use, you don't need to call the flush method because the events are sent every 60 seconds in the background.
User attributes configuration
This feature will give you robust and granular control over what users can see on your application. You can add rules using these attributes on the console UI's feature flag's targeting tab. See more.
- Swift
do {
// User attributes configuration
let attributes = [
"app_version": "1.0.0",
"os_version": "11.0.0",
"device_model": "iphone-12",
"language": "english",
"genre": "female"
]
let user = try BKTUser.Builder()
.with(id: "USER_ID")
.with(attributes: attributes)
.build()
BKTClient.initialize(config: config, user: user)
let client = BKTClient.shared
} catch {
// Error handling
}
Updating user attributes
This method will update all the current user attributes. This is useful in case the user attributes update dynamically on the application after initializing the SDK.
- Swift
let attributes = [
"app_version": "1.0.1",
"os_version": "11.0.0",
"device_model": "iphone-12",
"language": "english",
"genre": "female",
"country": "japan"
]
client.updateUserAttributes(attributes: attributes)
This updating method will override the current data.
Getting user information
This method will return the current user configured in the SDK. This is useful when you want to check the current user id and attributes before updating them through updateUserAttributes.
- Swift
let user = client.currentUser()
Getting evaluation details
This method will return the evaluation details for a specific feature flag or will return nil if the feature flag is missing in the SDK's cache.
This is useful if you use another A/B Test solution with Bucketeer and need to know the variation name, reason, and other information.
Evaluation details
- Swift
public struct Evaluation {
public let id: String
public let featureID: String
public let featureVersion: Int
public let userID: String
public let variationID: String
public let variationValue: String
public let reason: Int
}
Do not call this method without calling the Evaluating user method. The Evaluating user method must always be called because it generates analytics events that will be sent to the server.
- Swift
let evaluationDetails = client.evaluationDetails(featureId: "YOUR_FEATURE_FLAG_ID")
Listening to evaluation updates
The SDK can notify when the evaluation is updated. The listener can detect both automatic polling and manual fetching.
The listener callback is called on the main thread.
- Swift
class EvaluationUpdateListenerImpl: EvaluationUpdateListener {
// The listener callback is called on the main thread
func onUpdate() {
let client = BKTClient.shared
let showNewFeature = client.boolVariation(featureId: "YOUR_FEATURE_FLAG_ID", defaultValue: false)
if (showNewFeature) {
// The Application code to show the new feature
} else {
// The code to run when the feature is off
}
}
}
let listener = EvaluationUpdateListenerImpl()
// The returned key value is used to remove the listener
let key = client.addEvaluationUpdateListener(listener: listener)
// Remove a listener associated with the key
client.removeEvaluationUpdateListener(key: key)
// Remove all listeners
client.clearEvaluationUpdateListeners()
Background mode
This feature is optional but can be used by configuring the Background Mode in your application.
The default setting is 1 hour and a minimum of 20 minutes for the backgroundPollingInterval
and 1 minute for the eventsFlushInterval
settings.
Configuration
1. Open the XCode project setting.
2. Select your app target.
3. Select the Signing & Capabilities
settings tab.
4. Enable the Background processing
option in the Background Modes
section.
5. Register the identifier by adding the Permitted background task scheduler identifiers
option in the Info.plist, and set the following task IDs as the values.
- io.bucketeer.background.fetch.evaluations (Background task to fetch the latest evaluations from the server).
- io.bucketeer.background.flush.events (Background task to flush events generated by the client).
6. Add the code to enable the background task.
- Storyboard
- SwiftUI
Open the AppDelegate.swift
and add the following code to the didFinishLaunchingWithOptions
function.
import UIKit
import Bucketeer
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Add the code to enable background tasks
if #available(iOS 13.0, tvOS 13.0, *) {
BKTBackgroundTask.enable()
}
// Your app logic code
return true
}
}
Open your App.
swift` file and add the following code.
import SwiftUI
import Bucketeer
@main
struct ExampleSwiftUIApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
// Add the code to enable background tasks
.enableBKTBackgroundTask()
}
}
7. If needed, change the background polling default interval by adding the backgroundPollingInterval
setting in the BKTConfig as follows:
do {
// SDK configuration
let config = try BKTConfig.Builder()
.with(apiKey: "YOUR_API_KEY")
.with(apiEndpoint: "YOUR_API_ENDPOINT")
.with(featureTag: "YOUR_FEATURE_TAG")
.with(appVersion: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String)
.with(backgroundPollingInterval: 1800) // 30 minutes
.build()
} catch {
// Error handling
}
8. Please check the iOS Documentation for more details.
Destroying client
There are cases you might want to switch the user ID or reduce resources when the application is in the background.
For those cases, you can call the destroy interface, which will clear the client instance.
- Swift
do {
try BKTClient.destroy()
} catch {
// Error handling
}
If you want to switch the user ID, please call the flush interface before calling the destroy, so that all the pending events can be sent before clearing the client instance, then call the initialize interface with the new user information.