You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Swift implementation of the Eppo Randomization and Feature Flagging SDK.
3
+
Eppo's open source Swift SDK can be used for both feature flagging and experiment assignment.
4
4
5
5
The repository is hosted at [https://github.com/Eppo-exp/eppo-ios-sdk](https://github.com/Eppo-exp/eppo-ios-sdk)
6
6
7
-
## 1. Install the SDK
7
+
## Getting Started
8
+
9
+
### Installation
8
10
9
11
While in XCode:
10
12
@@ -13,23 +15,49 @@ While in XCode:
13
15
> 3. Set dependency rule to `Up to Next Minor Version` and select `Add Package`
14
16
> 4. Add to your project's target.
15
17
16
-
## 2. Initialize the SDK
18
+
### Usage
19
+
20
+
Begin by initializing a singleton instance of Eppo's client with a key from the [Eppo interface](https://eppo.cloud/feature-flags/keys). The client will configure itself and perform a network request to fetch the latest flag configurations. Once initialized, the client can be used to make assignments anywhere in your app.
17
21
18
-
Initialize with a SDK key, which can be created in the Eppo web interface:
During initialization, the SDK sends an API request to Eppo to retrieve the most recent experiment configurations such as variation values and traffic allocation. The SDK stores these configurations in memory so that assignments are effectively instant. For more information, see the [architecture overview](/sdks/architecture) page.
30
+
(It is recommended to wrap initialization in a `Task` block in order to perform network request asynchronously)
25
31
26
-
If you are using the SDK for experiment assignments, make sure to pass in an assignment logging callback (see [section](#define-an-assignment-logger-experiment-assignment-only) below).
32
+
#### Assign anywhere
33
+
34
+
```swift
35
+
let assignment =try eppoClient.getStringAssignment(
36
+
flagKey: "new-user-onboarding",
37
+
subjectKey: user.id,
38
+
subjectAttributes: user.attributes,
39
+
defaultValue: "control"
40
+
);
41
+
```
27
42
28
-
### Define an assignment logger (experiment assignment only)
43
+
During initialization, the SDK sends an API request to Eppo to retrieve the most recent experiment configurations: variation values, traffic allocation, etc. The SDK stores these configurations in memory, meaning assignments are effectively instant (accordingly, assignment is a synchronous operation). For more information, see the [architecture overview](/sdks/architecture) page.
44
+
45
+
Eppo's SDK also supports providing the configuration directly at initialization. For more information, see the [initialization modes](#initialization-modes) section below.
46
+
47
+
### Connecting an event logger
48
+
49
+
Eppo is architected so that raw user data never leaves your system. As part of that, instead of pushing subject-level exposure events to Eppo's servers, Eppo's SDKs integrate with your existing logging system. This is done with a logging callback function defined at SDK initialization:
50
+
51
+
```swift
52
+
eppoClient =tryawait EppoClient.initialize(
53
+
sdkKey: "mock-sdk-key",
54
+
assignmentLogger: segmentAssignmentLogger
55
+
)
56
+
```
29
57
30
-
If you are using the Eppo SDK for experiment assignment (i.e randomization), pass in an `assignmentLogger`to the constructor on SDK initialization. The SDK will invoke this function to capture assignment data whenever a variation is assigned.
58
+
This logger takes an [analytic event](#assignment-logger-schema) created by Eppo, `assignment`, and writes it to a table in the data warehouse (Snowflake, Databricks, BigQuery, or Redshift). You can read more on the [Event Logging](/sdks/event-logging) page.
31
59
32
-
The code below illustrates an example implementation of a logging callback using Segment. You could also use your own logging system, the only requirement is that the SDK receives a `logAssignment`function which sends data into a table in your warehouse which Eppo has read access to. Here we create an instance of an `AssignmentLogger` and configure the `EppoClient` to use this logger:
60
+
The code below illustrates an example implementation of a logging callback using Segment. You could also use your own logging system, the only requirement is that the SDK receives a function that takes Eppo's `Assignment` object and writes it to your warehouse. For details on the object's schema, see the [Assignment Logger Schema](#assignment-logger-schema) section below.
Now that the SDK is initialized and connected to your event logger, you can check what variant a specific subject (typically user) should see by calling the `get<Type>Assignment` functions.
84
+
85
+
For example, for a string-valued flag, use `getStringAssignment`:
let assignment =try eppoClient.getStringAssignment(
89
+
flagKey: "new-user-onboarding",
90
+
subjectKey: user.id,
91
+
subjectAttributes: user.attributes,
92
+
defaultValue: "control"
93
+
);
53
94
```
54
95
55
-
The SDK will invoke the `logAssignment` function with an `Assignment` object that contains the following fields:
96
+
Note that Eppo uses a unified API for feature gates, experiments, and mutually exclusive layers. This makes it easy to turn a flag into an experiment or vice versa without having to do a code release.
|`allocation` (string) | An Eppo allocation key | "allocation-17" |
60
-
|`experiment` (string) | An Eppo experiment key | "recommendation-algo-allocation-17" |
61
-
|`featureFlag` (string) | An Eppo feature flag key | "recommendation-algo" |
62
-
|`variation` (string) | The experiment variation the subject was assigned to | "control" |
63
-
|`subject` (string) | An identifier of the subject or user assigned to the experiment variation | UUID |
64
-
|`timestamp` (string) | The time when the subject was assigned to the variation | 2021-06-22T17:35:12.000Z |
65
-
|`subjectAttributes` (map) | A free-form map of metadata about the subject. These attributes are only logged if passed to the SDK assignment function |`{ "country": "US" }`|
98
+
The `getStringAssignment` function takes four inputs to assign a variation:
66
99
67
-
:::note
68
-
More details about logging and examples (with Segment, Rudderstack, mParticle, and Snowplow) can be found in the [event logging](/sdks/event-logging/) page.
69
-
:::
100
+
-`flagKey` - The key for the flag you are evaluating. This key is available on the feature flag detail page (see below).
101
+
-`subjectKey` - A unique identifier for the subject being experimented on (e.g., user), typically represented by a UUID. This key is used to deterministically assign subjects to variants.
102
+
-`subjectAttributes` - A map of metadata about the subject used for [targeting](/feature-flagging/concepts/targeting/). If targeting is not needed, pass in an empty object.
103
+
-`defaultValue` - The value that will be returned if no allocation matches the subject, if the flag is not enabled, if `getStringAssignment` is invoked before a configuration has been loaded, or if the SDK was not able to retrieve the flag configuration. Its type must match the `get<Type>Assignment` call.
70
104
71
105
72
-
##3. Assign Variations
106
+
#### Considerations
73
107
74
-
Assigning users to flags or experiments with a single `getStringAssignment` function:
108
+
For applications, wrapping assignment in an `ObservableObject` is the best practice. This will create an object that will update Swift UI when the assignment is received.
It is recommended to wrap initialization in a `Task` block in order to perform network request asynchronously.
93
-
94
-
For applications wrapping initialization and assignment in an `ObservableObject` is recommended. This will create an object that will update Swift UI when the assignment is received.
131
+
You can also choose to combinate instantiation and assignment within the same `ObservableObject`; the internal state will ensure only a single object and network request is created.
95
132
96
133
```swift
97
134
@MainActor
98
-
classAssignmentObserver: ObservableObject {
135
+
publicclassAssignmentObserver: ObservableObject {
99
136
@Publishedvar assignment: String?
100
137
101
138
publicinit() {
102
-
do {
103
-
let client =try EppoClient.shared()
104
-
self.assignment=try client.getStringAssignment(
105
-
flagKey: "ios-test-app-treatment",
106
-
subjectKey: "test-user",
107
-
subjectAttributes: ["country":"US"],
108
-
defaultVariation: "control"
109
-
);
110
-
} catch {
111
-
self.assignment=nil;
139
+
Task {
140
+
do {
141
+
// The initialization method has controls to maintain a single instance.
The `getStringAssignment` function takes four required parameters to assign a variation:
157
+
Rendering the view:
118
158
119
-
-`flagKey` - This key is available on the detail page for both flags and experiments. Can also be an experiment key.
120
-
-`subjectKey` - The entity ID that is being experimented on.
121
-
-`subjectAttributes` - An optional map of metadata about the subject used for targeting. If you create rules based on attributes on a flag/experiment, those attributes should be passed in on every assignment call.
122
-
-`defaultVariation` - The default variation to return if the SDK is unable to make an assignment.
// You can perform additional actions on appear if needed
179
+
}
180
+
}
181
+
}
182
+
```
123
183
124
184
### Typed assignments
125
185
126
-
Additional functions are available:
186
+
Every Eppo flag has a return type that is set once on creation in the dashboard. Once a flag is created, assignments in code should be made using the corresponding typed function:
127
187
128
188
```swift
129
189
getBooleanAssignment(...)
130
-
getIntegerAssignment(...)
131
190
getNumericAssignment(...)
191
+
getIntegerAssignment(...)
192
+
getStringAssignment(...)
132
193
getJSONStringAssignment(...)
133
194
```
195
+
196
+
Each function has the same signature, but returns the type in the function name. The only exception is `defaultValue`, which should be the same type as the flag. For boolean flags for instance, you should use `getBooleanAssignment`, which has the following signature:
197
+
198
+
```swift
199
+
funcgetBooleanAssignment(
200
+
flagKey: String,
201
+
subjectKey: String,
202
+
subjectAttributes: [String: Any],
203
+
defaultValue: Bool
204
+
) ->Bool
205
+
```
206
+
207
+
## Initialization Modes
208
+
209
+
When initializing the SDK, you can pass in a pre-fetched flag configuration JSON string:
210
+
211
+
```swift
212
+
Task {
213
+
tryawait EppoClient.initialize(
214
+
sdkKey: "SDK-KEY-FROM-DASHBOARD",
215
+
initialConfiguration: tryConfiguration(
216
+
flagsConfigurationJson: Data(#"{ "pre-loaded-JSON": "passed in here" }"#.utf8),
217
+
obfuscated: false
218
+
)
219
+
);
220
+
}
221
+
```
222
+
223
+
This will still perform a network request to fetch the latest flag configurations. The provided configuration will be used until the network request is successful.
224
+
225
+
If you'd instead like to initialize Eppo's client without performing a network request, you can pass in a pre-fetched configuration JSON string and use the `initializeOffline` method:
226
+
227
+
```swift
228
+
try EppoClient.initializeOffline(
229
+
sdkKey: "SDK-KEY-FROM-DASHBOARD",
230
+
initialConfiguration: tryConfiguration(
231
+
flagsConfigurationJson: Data(#"{ "pre-loaded-JSON": "passed in here" }"#.utf8),
232
+
obfuscated: false
233
+
)
234
+
);
235
+
```
236
+
237
+
The `obfuscated` parameter is used to inform the SDK if the flag configuration is obfuscated.
238
+
239
+
This initialization method is synchronous and allows you to perform assignments immediately.
240
+
241
+
### Fetching the configuration from the remote source on-demand
242
+
243
+
After the client has been initialized, you can use `load()` to asynchronously fetch the latest flag configuration from the remote source.
244
+
245
+
```swift
246
+
try EppoClient.initializeOffline(
247
+
sdkKey: "SDK-KEY-FROM-DASHBOARD",
248
+
initialConfiguration: tryConfiguration(
249
+
flagsConfigurationJson: Data(#"{ "pre-loaded-JSON": "passed in here" }"#.utf8),
250
+
obfuscated: false
251
+
)
252
+
);
253
+
254
+
...
255
+
256
+
Task {
257
+
tryawait EppoClient.shared().load();
258
+
}
259
+
```
260
+
261
+
As modern iOS devices have substantial memory, applications are often kept in memory across sessions. This means that the flag configurations are not automatically reloaded on subsequent launches.
262
+
263
+
It is recommended to use the `load()` method to fetch the latest flag configurations when the application is launched.
264
+
265
+
266
+
## Assignment Logger Schema
267
+
268
+
269
+
The SDK will invoke the `logAssignment` function with an `Assignment` object that contains the following fields:
|`allocation` (string) | An Eppo allocation key | "allocation-17" |
274
+
|`experiment` (string) | An Eppo experiment key | "recommendation-algo-allocation-17" |
275
+
|`featureFlag` (string) | An Eppo feature flag key | "recommendation-algo" |
276
+
|`variation` (string) | The experiment variation the subject was assigned to | "control" |
277
+
|`subject` (string) | An identifier of the subject or user assigned to the experiment variation | UUID |
278
+
|`timestamp` (string) | The time when the subject was assigned to the variation | 2021-06-22T17:35:12.000Z |
279
+
|`subjectAttributes` (map) | A free-form map of metadata about the subject. These attributes are only logged if passed to the SDK assignment function |`{ "country": "US" }`|
280
+
281
+
:::note
282
+
More details about logging and examples (with Segment, Rudderstack, mParticle, and Snowplow) can be found in the [event logging](/sdks/event-logging/) page.
0 commit comments