Testing
Flutter SDK
Run the SDK against a local worker, fire deep links at the emulator and simulator, test install attribution end to end on a real device, and check the list before shipping.
A test environment for debug builds
Section titled “A test environment for debug builds”Create a test environment in the dashboard (Settings → Test environment): a second account, t-<username>, with the same apps. Give debug builds its SDK key, and its link host in their associated domains (applinks:t-acme.adto.app and the Android intent filter), so nothing you test reaches your live numbers:
await dectog.initialize(DectogConfig( apiKey: kReleaseMode ? liveSdkKey : testSdkKey, baseUrl: 'https://app.adto.app', linkRootDomain: 'adto.app',));The key alone decides which account gets the data: there is nothing else to set. DectogConfig(environment:) (live or test) is an optional check, not a switch, and changes nothing at runtime: when the app calls diagnostics(), it warns if the key belongs to the other environment. Without it, diagnostics() still warns when a release build carries a test key. In the dashboard, the Live / Test switch at the top of the sidebar shows either account.
Testing link handling
Section titled “Testing link handling”Three delivery paths need to work, and each exercises different code:
| Scenario | How to test | What to expect |
|---|---|---|
| Cold start | Kill the app, open a link | onLinkClick emits once and getInitialLink() returns the same click. A second getInitialLink() returns the same object without a second click report. |
| Warm start | Background the app, open a link | onLinkClick emits; the open is reported with POST /api/v1/app-opens. |
| Running | Open a link while the app is in the foreground | Same as warm start. The same URL delivered twice within 10 seconds counts once. |
Also check an OAuth-style URL (any scheme ending in a fb<digits> form, or a URL carrying access_token): nothing is emitted and nothing is reported.
End-to-end install attribution
Section titled “End-to-end install attribution”Install attribution only works for a build that was installed the way your users install it. Simulators and emulators cannot install from a store, and a build installed with flutter run or adb install has no campaign referrer (the Play read ends organic or not_available).
- In Dashboard → Apps, save the real App Store and Play Store URLs and the SHA-256 fingerprint of the certificate the installed build is signed with (the Play App Signing key for a store build). Turn on iOS tap-to-store under Advanced if you want the deterministic pasteboard path on iPhone.
- Create a link in Dashboard → Links.
- Uninstall the app from a physical device. On Android,
adb shell pm clear <package>also resets the SDK’s state without uninstalling. - Tap the link on that device. Android lands in Play with
referrer=dtg_click=…&dtg_l=…on the store URL. iOS lands on the tap-to-store page; “Get the app” copies the link with its click id and opens the App Store. - Install from the store and open the app once.
onAppInstalledfires withmatched: true,matchConfidence: 'guaranteed',slugnaming the link, andmatchSourcereferrer(Android,referrerSource: 'google_play') orclipboard(iOS). On iOS 16 and newer the first launch shows the “Allow paste” alert; decline it to test theDectogPasteButtonrecovery path. - Without a deterministic signal (pasteboard declined, referrer unavailable) the server falls back to the fingerprint: the install must come from the same network as the tap, within 24 hours (2 hours for cellular installs, or clicks with neither a device model nor a screen profile to compare with the install). Expect
matchConfidencehighwithmatched: true, ormediumwithroutingonly. - Check the Overview: the install appears in the Installs number and in the attribution-quality panel, whose referrer-status chips show how the Android read ended.
To retest, uninstall and repeat. Each test consumes its click: a click already credited to an install is never credited again, so tap a fresh link each time.
Things that look like failures and are not:
- The verdict is
matched: falsewithinstallState: 'update': the device already had the app before this build. Updates are never fingerprint-matched. Uninstall first. isReinstall: true: the server has seen this device id on an earlier install of your account. Reinstalls still attribute; the dashboard counts them under Reinstalls, next to Installs, which counts first installs.appleSearchAds.statusstayspendingin the first-launch process. CallgetAttributionData()on a later launch.fraudFlagscontainsinstall_velocity_ipafter many test installs from one network,emulatoron a simulator or emulator, andsigning_cert_mismatchon a debug build whose key the app’s SHA-256 fingerprints do not list. When the app blocks one of those rules (Apps, Fraud rules), those installs count as organic: untick the rule while you test. See Fraud rules.
Ship checklist
Section titled “Ship checklist”- App record complete: bundle ID and Team ID (iOS), package and the release / Play App Signing fingerprint (Android), real store URLs. Both Verify buttons on the Apps page report your app IDs and packages with fingerprints present.
-
applinks:{username}.adto.appin every entitlements file your build configurations use; anautoVerifyintent filter for{username}.adto.appin the manifest;adb shell pm get-app-links <package>reportsverified. - Custom scheme registered on both platforms if you saved one in the app record.
- SDK key (
dtg_pub_…, not the account key) injected at build time, not committed.baseUrl: 'https://app.adto.app'andlinkRootDomain: 'adto.app'set, since the SDK’s defaults are the old DecTog service. -
collect,advertisingIdProviderandpersistDeviceIdchosen deliberately, and the privacy policy, Play Data safety form and App Store privacy labels updated for what you send. See Device data and privacy. - If you ship through the Galaxy Store or GetApps, their referrer libraries added to the app module. See Installation.
-
identifycalled after signup and login,trackConversionon purchases, so leads and revenue reach the dashboard. -
onAppInstalledhandled as a stream that can emit more than once; routing taken fromroutingordeepLink, credit only frommatched. - At least one attributed install verified on a real device per platform.
-
enableDebugLoggingoff in release builds.
More symptoms and their causes are collected on Troubleshooting.