initialize method

Future<void> initialize(
  1. DigiaConfig config
)

Implementation

Future<void> initialize(DigiaConfig config) async {
  if (_initialized) {
    debugPrint('[Digia] initialize() called more than once — ignored.');
    return;
  }
  _config = config;
  _themeMode = config.themeMode;
  CampaignColorResolver.shared.setThemeMode(_themeMode);
  _initialized = true;
  _sdkState = SDKState.initializing;

  EngageFonts.configure(fontFamily: config.fontFamily);

  WidgetsBinding.instance.addObserver(this);
  _registerDebugDeepLinkObserver();
  _handleColdStartDebugDeepLink();
  registerDebugDeepLinkRelayHandler(_consumeRelayedDebugDeepLink);
  unawaited(_consumeRelayedDebugDeepLink());

  // Construct the guide manager now so it registers the showcase controller
  // and listens to the orchestrator before any campaign is routed.
  _guideManager;

  // The whole init runs under one guard (mirrors Android's DigiaInstance):
  // any failure leaves the SDK in [SDKState.failed] and resets the
  // started flag so a later call can retry.
  try {
    // Resolve the engage API host from `environment` before ANY network call —
    // campaign fetch, analytics track, and session reporting all read the
    // URLs from [DigiaEndpoints]. Mirrors Android's endpoint configuration.
    DigiaEndpoints.configure(config);
    DigiaLog.configure(config.logLevel);

    EngageActionRunner.shared.configureActionHandlers(config.actionHandlers);

    // ── Resolve base URL (sandbox → dev.digia.tech, prod → app.digia.tech) ─
    DigiaEndpoints.configure(config);

    // ── Fetch + cache engage campaigns ──────────────────────────────────
    _identityInitialization = _initializeIdentity(config);
    await _identityInitialization;
    captureTextNotifier.value = PreferencesStore.instance.read<bool>(
          'anchorless_capture.include_text',
          false,
        ) ??
        false;
    captureMediaNotifier.value = PreferencesStore.instance.read<bool>(
          'anchorless_capture.include_media',
          false,
        ) ??
        false;
    captureStructureNotifier.value = PreferencesStore.instance.read<bool>(
          'anchorless_capture.include_structure',
          false,
        ) ??
        false;
    final deviceId = EngageSettings.instance.getUuid();
    _dio.options = BaseOptions(
      connectTimeout: const Duration(seconds: 10),
      receiveTimeout: const Duration(seconds: 10),
      headers: {
        ...DigiaAnalyticsService.instance.requestHeaders,
        Headers.contentTypeHeader: Headers.jsonContentType,
      },
      // Campaign fetching reads `statusCode` itself and raises its own typed
      // failure, so Dio must not throw first. This is a client-wide default
      // though, not a per-request one — see the caveat on `_dio` before
      // routing anything else through it.
      validateStatus: (_) => true,
    );
    _submissionReporter = SubmissionReporter(config, deviceId);
    _componentRegistry.configure(config, deviceId);
    captureModeNotifier.value = _componentRegistry.isEnabled;
    await _componentRegistry.setEnabled(captureModeNotifier.value);
    // With the documented `await Digia.initialize(...)`-then-`runApp` flow
    // this resolves before the first build, so a TestFlight (release-mode)
    // install is already debug-eligible when DigiaRecordingBadge first builds.
    await cacheInstallerStoreDebugEligibility();
    DigiaDebugOverlayController.instance.configure();
    LiveTestService.instance.configure(
      config,
      deviceId,
      onCampaignTest: handleLiveTestCampaign,
    );
    final campaignApiService = DioCampaignApiService(_dio);
    final campaignBundle =
        await CampaignFetcher(campaignApiService).fetchBundle();
    _serverTimeClock = campaignBundle.serverClock;
    final campaigns = campaignBundle.parse();
    _campaignDesignTokens = campaignBundle.designTokens;
    _campaignStore.populate(campaigns);

    // ── Ready ───────────────────────────────────────────────────────────
    _sdkState = SDKState.ready;
    if (_campaignStore.isEmpty) {
      debugPrint('[Digia] No campaigns fetched — CampaignStore is empty.');
    } else {
      _logIfVerbose('Fetched ${campaigns.length} campaign(s).');
    }
    _flushPendingPayloadIfAny();

    _logIfVerbose(
      'Digia initialized with apiKey=${config.apiKey}, '
      'environment=${config.environment.name}.',
    );
  } catch (e) {
    _initialized = false;
    _sdkState = SDKState.failed;
    _campaignDesignTokens = DesignTokenCatalog.empty;
    debugPrint('[Digia] initialize failed: $e');
    // A template presented during init left a CEP slot held (we returned
    // `true` from onCampaignTriggered). Init failed so it will never route —
    // release the slot, else later in-apps queue forever.
    final pending = _pendingPayload;
    _pendingPayload = null;
    if (pending != null) {
      _events.toCep(const ExperienceDismissed(), pending);
    }
  }
}