Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions docs/platform-guides/android/integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,55 @@ Adding the `usePreviewCollection` flag allows the builder to configure a `Nimbus
}.build(appInfo)
```

### Connecting to the Recorded Targeting Context

The `recordedContext` builder option connects the Nimbus SDK to a `RecordedContext` implementation for behavioral targeting and Glean recording. This replaces the older `customTargetingAttributes` approach for providing targeting attributes.

```kotlin
return NimbusBuilder(context).apply {
// …
recordedContext = RecordedNimbusContext(isFirstRun = isAppFirstRun)
// …
}.build(appInfo)
```

See [Recording Targeting Context](/advanced/recording-targeting-context) for details on implementing the `RecordedContext` trait.

### Reporting malformed feature configuration

If your app detects that a feature configuration from an experiment is invalid or malformed, you can report it as telemetry using `recordMalformedConfiguration`:

```kotlin
FxNimbus.features.myFeature.recordMalformedConfiguration(partId = "invalid-field")
```

This sends a `malformedConfiguration` Glean event identifying the feature and the specific part that was invalid.

## Unit and UI testing with `HardcodedNimbusFeatures`

The `HardcodedNimbusFeatures` class lets you inject feature configurations directly for unit and UI testing, without a running Nimbus SDK or network connection:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence reads strangely. "Without running Nimbus SDK" ?


```kotlin
val hardcodedNimbus = HardcodedNimbusFeatures(testContext,
"my-feature" to JSONObject("""{"enabled": true, "title": "Hello"}""")
)
hardcodedNimbus.connectWith(FxNimbus)

// Access feature values as normal — they'll use the hardcoded config
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Access feature values as normal — they'll use the hardcoded config
// Access feature values as normal — it will use the hardcoded config.

val config = FxNimbus.features.myFeature.value()

// Test assertions
assertTrue(hardcodedNimbus.isExposed("my-feature"))
assertEquals(1, hardcodedNimbus.getExposureCount("my-feature"))
assertFalse(hardcodedNimbus.isMalformed("my-feature"))
```

Key test methods:
- `isExposed(featureId)` — whether `recordExposureEvent` was called
- `getExposureCount(featureId)` — number of exposure events recorded
- `isMalformed(featureId)` — whether `recordMalformedConfiguration` was called
- `hasFeature(featureId)` — whether the feature was provided to the constructor

## A complete `NimbusBuilder` example

```kotlin
Expand All @@ -232,6 +281,8 @@ Adding the `usePreviewCollection` flag allows the builder to configure a `Nimbus
usePreviewCollection = context.settings().nimbusUsePreview
isFirstRun = isAppFirstRun
sharedPreferences = context.settings().preferences
recordedContext = RecordedNimbusContext(isFirstRun = isAppFirstRun)
featureManifest = FxNimbus
// Optional callbacks.
onCreateCallback = { nimbus ->
// called when nimbus is set up
Expand Down
12 changes: 9 additions & 3 deletions docs/platform-guides/android/mobile-ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,24 @@ Required user interface components for apps integrating with the Nimbus SDK.

Currently Nimbus provides no user-interface components of its own, though provides API to connect to existing settings screens.

## Global Opt-Out/Opt-In for Experiments
## Opt-Out/Opt-In Controls

The settings page should include a `Studies` toggle, which allows users to opt-in or opt-out of experiments. The example from Firefox for iOS is shown:

<img src="/img/firefox-ios/studies-toggle.png" width="300px" />

Toggling the `Studies` flag should set the `Nimbus` value for `globalUserParticipation`:
Experiment participation and rollout participation are controlled separately:

```kotlin
nimbus.globalUserParticipation = flag
// Controls opt-in/out for experiments (not rollouts)
nimbus.experimentParticipation = flag

// Controls opt-in/out for rollouts (not experiments)
nimbus.rolloutParticipation = flag
```

When set to `false`, the user will be opted out of all active enrollments of that type and will not be enrolled in new ones. Toggling the `Studies` flag should set `experimentParticipation`.

## Resetting Telemetry Identifiers

During experiment enrollment, telemetry is generated which can connect the user to the experiment enrollment.
Expand Down
20 changes: 16 additions & 4 deletions docs/platform-guides/ios/integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,20 @@ To connect the `NimbusInterface` object to the command line, we need to feed the
.build(appInfo: appInfo)
```

### Connecting to the Recorded Targeting Context

The `recordedContext` builder option connects the Nimbus SDK to a `RecordedContext` implementation for behavioral targeting and Glean recording. This replaces the older `customTargetingAttributes` approach for providing targeting attributes.

```swift
return NimbusBuilder(dbPath: dbPath)
// …
.with(recordedContext: RecordedNimbusContext(isFirstRun: isFirstRun))
// …
.build(appInfo: appSettings)
```

See [Recording Targeting Context](/advanced/recording-targeting-context) for details on implementing the `RecordedContext` protocol.

## A complete `NimbusBuilder` example

```swift
Expand All @@ -203,10 +217,7 @@ public static var nimbus: NimbusInterface = {
// thinks it is.
let appSettings = NimbusAppSettings(
appName: "example-app",
channel: "release",
customTargetingAttributes: [
"is_first_run": isFirstRun,
]
channel: "release"
)

let errorReporter: NimbusErrorReporter = { err in
Expand All @@ -229,6 +240,7 @@ public static var nimbus: NimbusInterface = {
.with(errorReporter: errorReporter)
.with(initialExperiments: Bundle.main.url(forResource: "initial_experiments", withExtension: "json"))
.isFirstRun(isFirstRun)
.with(recordedContext: RecordedNimbusContext(isFirstRun: isFirstRun))
.with(bundles: bundles)
.with(userDefaults: UserDefaults.standard)
.with(featureManifest: AppConfig.shared)
Expand Down