create static method
Future<SDKServices>
create({
- required DigiaConfig config,
- required LocalStorage storage,
- required NetworkClientFactory networkClient,
- required void onLiveTestCampaign(
- LiveTestInvocation invocation
- required AnalyticsQueue analyticsQueue,
- required AnalyticsQueue appEventAnalyticsQueue,
- Map<
String, dynamic> ? staticContext, - int clock()?,
The composition root: builds every service from config and storage.
Async only to read the static device/app context once, up front, so no
service is ever observed half-built.
storage must be the warmed root storage, after LocalStorageMigrator
has run. This is the only place storage is scoped per domain.
The process singletons the SDK still has (component registry, live test,
debug overlay) get their storage and client here too.
analyticsQueue and appEventAnalyticsQueue are passed in, not built:
each owns a database file and must outlive this container across
re-initialization. dispose leaves them open.
Implementation
static Future<SDKServices> create({
required DigiaConfig config,
required LocalStorage storage,
required NetworkClientFactory networkClient,
required void Function(LiveTestInvocation invocation) onLiveTestCampaign,
required AnalyticsQueue analyticsQueue,
required AnalyticsQueue appEventAnalyticsQueue,
Map<String, dynamic>? staticContext,
int Function()? clock,
}) async {
if (staticContext == null) {
try {
staticContext = await DigiaAnalyticsService.buildStaticContext();
} catch (error) {
// Degrade, don't fail init: events and headers go out without it.
_log.e('Static context unavailable — continuing', error: error);
staticContext = const {};
}
}
final Map<String, dynamic> resolvedStaticContext = staticContext;
final systemDeviceId = await IdentityManager.systemDeviceId();
final identityManager = IdentityManager.create(
storage: storage.scoped('identity'),
newDeviceId: () => systemDeviceId,
);
final sessionManager = SessionManager.create(
storage: storage.scoped('session'),
timeoutMs: config.analyticsConfig.sessionTimeoutMs,
clock: clock,
);
final sessionLifecycleObserver = SessionLifecycleObserver(sessionManager);
WidgetsBinding.instance.addObserver(sessionLifecycleObserver);
final headers = EngageHeaders(
config: config,
identityManager: identityManager,
sessionManager: sessionManager,
staticContext: resolvedStaticContext,
);
final client = networkClient(headers.call);
final baseAnalyticsConfig = config.analyticsConfig;
final analyticsConfig =
(DigiaEndpoints.isTestMode && !config.isAnalyticsConfigExplicit)
? DigiaAnalyticsConfig(
enabled: baseAnalyticsConfig.enabled,
flushBatchSize: 1,
flushIntervalMs: 100,
maxBatchSize: baseAnalyticsConfig.maxBatchSize,
queueMaxEvents: baseAnalyticsConfig.queueMaxEvents,
sessionTimeoutMs: baseAnalyticsConfig.sessionTimeoutMs,
)
: baseAnalyticsConfig;
final backgroundTime = defaultTargetPlatform == TargetPlatform.iOS
? PluginBackgroundTime()
: const NoBackgroundTime();
final analyticsService = DigiaAnalyticsService(
config: analyticsConfig,
identityManager: identityManager,
sessionManager: sessionManager,
queue: analyticsQueue,
networkClient: client,
networkMonitor: PlatformNetworkMonitor(),
backgroundTime: backgroundTime,
staticContext: resolvedStaticContext,
);
final appEventsService = AppEventsService(DigiaAnalyticsService(
config: analyticsConfig,
identityManager: identityManager,
sessionManager: sessionManager,
queue: appEventAnalyticsQueue,
networkClient: client,
networkMonitor: PlatformNetworkMonitor(),
backgroundTime: backgroundTime,
staticContext: resolvedStaticContext,
maxQueueEvents: appEventQueueMaxEvents,
));
final sessionReporter = SessionReporter(
sessionId: () => sessionManager.sessionId,
anonymousId: () => identityManager.deviceId,
userId: () => identityManager.userId,
context: () => resolvedStaticContext,
networkClient: client,
storage: storage.scoped('session'),
);
final sessionIdentityWiring = SessionIdentityWiring(
identityManager: identityManager,
sessionManager: sessionManager,
sessionReporter: config.analyticsConfig.enabled ? sessionReporter : null,
)..attach();
DigiaDebugOverlayController.instance.configure(storage.scoped('debug'));
final componentRegistry = ComponentRegistryService.instance
..networkClient = client
..configure(config, storage.scoped('registry'));
final liveTestService = LiveTestService.instance
..configure(
onCampaignTest: onLiveTestCampaign,
storage: storage.scoped('live_test'),
networkClient: client,
);
return SDKServices(
storage: storage,
identityManager: identityManager,
sessionManager: sessionManager,
networkClient: client,
analyticsService: analyticsService,
appEventsService: appEventsService,
frequencyManager: FrequencyManager(
storage.scoped('frequency'),
() => sessionManager.sessionId,
),
campaignStore: CampaignStore(),
submissionReporter: SubmissionReporter(
networkClient: client,
sessionId: () => sessionManager.sessionId,
),
sessionReporter: sessionReporter,
sessionLifecycleObserver: sessionLifecycleObserver,
captureSession: CaptureSession(networkClient: client),
componentRegistry: componentRegistry,
liveTestService: liveTestService,
sessionIdentityWiring: sessionIdentityWiring,
captureStorage: storage.scoped('capture'),
);
}