reportInstall method
Reports a new app install to the LinkForty backend to retrieve attribution data.
Behavior:
- First Launch: If no
installIdis stored locally, it collects a device fingerprint and performs a POST request to/api/sdk/v1/install. - Subsequent Launches: If an
installIdexists, 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;
}