Integrate Analytics#
Storyteller delivers user activity events to your app through StorytellerDelegate.onUserActivityOccurred(type:data:). This guide shows how to retain the delegate, select the tracking behavior during initialization, forward events to your analytics layer, and add host-defined attribution context.
Use the Analytics Event Reference after setup to choose the event types and payload fields your analytics implementation needs.
Retain a Delegate and Forward Events#
Storyteller.shared.delegate is weak. Keep the delegate in app-owned state for as long as you need callbacks, and assign the same object that handles your other global Storyteller callbacks. If another part of the app later replaces Storyteller.shared.delegate, the original object stops receiving events.
The following complete example forwards the serialized event key (type.rawValue), selected content identifiers, and any analytics context into a provider-independent host analytics layer. Replace ConsoleAnalytics with your own analytics adapter.
import StorytellerSDK
struct HostAnalyticsEvent {
let name: String
let storyId: String?
let clipId: String?
let context: StorytellerAnalyticsContext?
}
protocol HostAnalyticsTracking: AnyObject {
func track(_ event: HostAnalyticsEvent)
}
final class ConsoleAnalytics: HostAnalyticsTracking {
func track(_ event: HostAnalyticsEvent) {
print(
"Storyteller event: \(event.name), "
+ "story: \(event.storyId ?? "none"), "
+ "clip: \(event.clipId ?? "none"), "
+ "context: \(event.context ?? [:])"
)
}
}
final class StorytellerAnalyticsDelegate: StorytellerDelegate {
private let analytics: any HostAnalyticsTracking
init(analytics: any HostAnalyticsTracking) {
self.analytics = analytics
}
func onUserActivityOccurred(
type: StorytellerUserActivity.EventType,
data: StorytellerUserActivityData
) {
analytics.track(
HostAnalyticsEvent(
name: type.rawValue,
storyId: data.storyId,
clipId: data.clipId,
context: data.context
)
)
}
}
@MainActor
final class StorytellerIntegration {
private let storytellerDelegate: StorytellerAnalyticsDelegate
init(analytics: any HostAnalyticsTracking) {
let delegate = StorytellerAnalyticsDelegate(analytics: analytics)
storytellerDelegate = delegate
Storyteller.shared.delegate = delegate
}
func initialize(
apiKey: String,
userId: String,
trackingOptions: StorytellerEventTrackingOptions
) async throws {
try await Storyteller.shared.initialize(
apiKey: apiKey,
userInput: StorytellerUserInput(externalId: userId),
eventTrackingOptions: trackingOptions
)
}
}
@MainActor
final class AppServices {
private let storyteller = StorytellerIntegration(
analytics: ConsoleAnalytics()
)
func start() async throws {
// Use .enableAll only when it matches your app's consent policy.
try await storyteller.initialize(
apiKey: "your-api-key",
userId: "your-user-id",
trackingOptions: .enableAll
)
}
}
StorytellerUserActivityData is an event-specific payload, so most properties are optional. Forward only the fields your analytics contract needs, using the event reference to determine which fields apply to each event. The Showcase app demonstrates provider-specific mapping in StorytellerTrackingDelegate.
Choose Tracking Options During Initialization#
Pass StorytellerEventTrackingOptions when you call initialize(...). The value is fixed for that initialization; reinitialize the SDK to apply a later consent change.
For host analytics delivery, these options have distinct effects:
| Option | Effect on onUserActivityOccurred |
|---|---|
enableUserActivityTracking |
Must be enabled for the integrating app to receive user activity events. |
enableAdTracking |
Must also be enabled for Ad-related user activity events. It does not control whether a host-supplied Ad loading callback is requested. |
enableStorytellerTracking |
Controls Storyteller's own analytics collection; enableUserActivityTracking remains the host callback gate. |
enableFullVideoAnalytics |
When disabled, callbacks still arrive, but content identifiers and titles listed in Privacy and Tracking are removed from their payloads. |
The other options affect personalization, viewing state, and functional behavior. Choose the complete configuration from your app's consent requirements; see Privacy and Tracking before changing defaults.
Add Analytics Context#
StorytellerAnalyticsContext is a type alias for [String: String]. The SDK does not prescribe its keys. Add a context dictionary to the configuration for the surface or presentation you want to attribute:
StorytellerStoriesListConfigurationStorytellerClipsListConfigurationStorytellerClipCollectionConfiguration, including UIKit and SwiftUI Embedded ClipsStorytellerCardConfigurationStorytellerHomeConfiguration
For example, an Embedded Clips configuration can identify both its screen and placement:
let clipsConfiguration = StorytellerClipCollectionConfiguration(
collectionId: "top-plays",
context: [
"screen": "home",
"placement": "primary-clips-feed"
]
)
The SDK carries the dictionary into StorytellerUserActivityData.context when an event can be attributed to that configured surface or to content opened from it. The property remains optional: events without an attributable configured surface do not receive a context value. Set context before loading or opening the content whose events you want to attribute.
See Context in the Analytics Event Reference for the complete attribution contract and another consumption example.
Verify the Integration#
After initialization succeeds:
- Load known published content using a configuration with a distinctive context value.
- Open or interact with that content to produce a documented event, such as
openedStoryoropenedClip. - Confirm your analytics adapter receives the expected
type.rawValueand payload. - Confirm
data.contextcontains the value supplied by the originating configuration when that event is attributable to the surface.
Initialization success alone does not exercise this callback path or generate a host user activity callback. Trigger a supported content interaction when testing the integration.
Troubleshoot Missing Events or Context#
If no event arrives:
- Confirm the app still strongly retains its delegate and that
Storyteller.shared.delegatehas not been replaced. - Confirm
enableUserActivityTrackingwas enabled during the current SDK initialization. - For an Ad event, also confirm
enableAdTrackingis enabled and that the corresponding Ad lifecycle point was actually reached. - Confirm the documented interaction occurred; loading and initialization callbacks are separate from user activity delivery.
If the callback arrives but data is missing:
- Check
enableFullVideoAnalyticsbefore treating absent Story, Page, Clip, or Card identifiers and titles as an SDK fault. - For missing context, confirm the active configuration supplied it before the content was loaded or opened and that the event can be attributed to that surface.
- Treat every event payload as event-specific; unrelated fields are expected to be
nil.
Use Callbacks or Analytics Events Do Not Arrive to separate analytics delivery from component loading, app navigation, and Ad loading callbacks.
Continue with the Event Reference#
Use the Analytics Event Reference for all public event keys, the common fields for each feature, event-specific fields, and enum value definitions.