reportInstall method

Future<InstallResponse> reportInstall({
  1. required int attributionWindowHours,
  2. String? deviceId,
  3. String? appToken,
})

Reports a new app install to the LinkForty backend to retrieve attribution data.

Behavior:

  • First Launch: If no installId is stored locally, it collects a device fingerprint and performs a POST request to /api/sdk/v1/install.
  • Subsequent Launches: If an installId exists, it skips the network call and returns the locally cached attribution data.
  • Error Handling: If the network call fails on the first launch, it gracefully treats the install as "organic" (no attribution) so the SDK remains functional. Re-attribution may be attempted on the next launch.

Parameters:

  • attributionWindowHours: The lookback window in hours for matching clicks.
  • deviceId: An optional persistent device ID (like IDFA or GAID).
  • appToken: An optional public workspace token (LinkForty Cloud) so the backend can scope attribution to the correct workspace.

Implementation

Future<InstallResponse> reportInstall({
  required int attributionWindowHours,
  String? deviceId,
  String? appToken,
}) async {
  final storedInstallId = _storageManager.getInstallId();
  if (storedInstallId != null) {
    final cachedData = _storageManager.getInstallData();
    LinkFortyLogger.log(
      'Subsequent launch — loading cached attribution (installId: $storedInstallId)',
    );
    return InstallResponse(
      installId: storedInstallId,
      attributed: cachedData != null,
      confidenceScore: cachedData != null ? 100.0 : 0.0,
      matchedFactors: const [],
      deepLinkData: cachedData,
    );
  }

  final fingerprint = await _fingerprintCollector.collectFingerprint(
    attributionWindowHours: attributionWindowHours,
    deviceId: deviceId,
  );

  // Attach the workspace token (when provided) so Cloud can scope attribution
  // to the correct workspace rather than relying solely on fingerprint matching.
  final body = appToken != null
      ? {...fingerprint.toJson(), 'appToken': appToken}
      : fingerprint.toJson();

  LinkFortyLogger.log('Reporting install with fingerprint: $fingerprint');

  InstallResponse response;
  try {
    response = await _networkManager.request<InstallResponse>(
      endpoint: '/api/sdk/v1/install',
      method: HttpMethod.post,
      body: body,
      fromJson: (json) => InstallResponse.fromJson(json),
    );
    LinkFortyLogger.log('Install response: $response');
  } on LinkFortyError catch (e) {
    LinkFortyLogger.log(
      'Install network call failed — treating as organic. Error: $e',
    );
    return InstallResponse(
      installId: '',
      attributed: false,
      confidenceScore: 0.0,
      matchedFactors: const [],
    );
  }

  await _storageManager.saveInstallId(response.installId);

  final deepLinkData = response.deepLinkData;
  if (deepLinkData != null) {
    await _storageManager.saveInstallData(deepLinkData);
    LinkFortyLogger.log(
      'Install attributed with confidence: ${response.confidenceScore}%',
    );
  } else {
    LinkFortyLogger.log('Organic install (no attribution)');
  }

  await _storageManager.setHasLaunched();

  return response;
}