Repository navigation
fix: retry failed install reports, React Navigation 7 ref types, debug-only logging, peer deps - #15
Merged
Merged
Conversation
…s.txt - Require React Native 0.65+, the floor set by AsyncStorage 2.x - Install AsyncStorage 2.x and device-info 15.x in the app itself, since autolinking only links an app's own dependencies and AsyncStorage 3.x registers a different native module name - Add the AppDelegate / SceneDelegate step that forwards links to RCTLinkingManager, and launchMode="singleTask" on Android - Explain that the server serves the AASA and assetlinks.json files - Use https://api.linkforty.com for the hosted service, mark appToken as required there, apiKey as only needed for createLink() - Await init() before onDeepLink() - Document trackRevenue, setExternalUserId, externalUserId, deduplicated, the lack of an offline event queue, and the data the SDK sends - Note what differs on self-hosted Core (createLink templateId, link hosts)
…onRef NavigationRouteLike.params was Record<string, unknown>, which React Navigation 7's Readonly<object | undefined> route params don't assign to, so createNavigationContainerRef() (without a param list) failed to type-check as navigationRef (TS2322, also with strict off), including the documented setup and examples/AutoNavTracking.tsx. Params are now typed Readonly<object | undefined>, and sanitizeScreenParams accepts any value and ignores non-objects and arrays at runtime. Also documents apiKey, appToken and debug without internal references, and drops references to private planning notes from comments.
The install-reported flag was saved before the install report was sent, so a report that failed (no connection, server error, app killed during it) was never sent again: the install ID stayed null and every trackEvent() was dropped. The flag is now saved only once the server accepts the report, so each later launch reports again until one succeeds. The storage key is unchanged, so installs already reported are not reported again. Logging: - DeepLinkHandler logged every URL and resolved link data on every link and cold start regardless of debug; it now logs only with debug. - init() with debug logged the whole config including the API key; the key is now redacted (and the navigation ref is not dumped). - Without debug, only failures are logged: a failed install report or event, init() called twice, an event dropped for lack of an install ID. clearData() now also removes the saved link hosts and fetches them again, and uses removeItem rather than multiRemove.
… AsyncStorage 3 React Native autolinking only links native modules listed in the app's own package.json, so apps must install both anyway. As regular dependencies they also let a package manager install a second, nested copy: an app on AsyncStorage 3 got a nested 2.x for the SDK, whose JS looks up the RNCAsyncStorage native module while 3.x registers RNAsyncStorage, so the SDK's storage was null at runtime. As peers the SDK shares the app's copy. AsyncStorage ^2.2.0 || ^3.0.0: the SDK only uses getItem, setItem and removeItem, which 3.x's default (legacy) storage keeps, reading the data 2.x wrote. react-native-device-info stays ^15.0.0. The react-native peer floor rises from 0.64 to 0.65, the floor AsyncStorage 2.x declares.
README and llms.txt: the install report is retried on later launches, what is logged with and without debug, the native modules as peer dependencies (AsyncStorage 2.x or 3.x), what clearData() removes, and that untyped React Navigation 7 refs type-check. Refers to the hosted service generically and removes ticket IDs and a private repository link from past CHANGELOG entries.
Each onDeepLink() call added another 'url' listener, so after a second
call every link reached the callback twice. The handler now keeps the
subscription from Linking.addEventListener and adds it only once;
calling onDeepLink() again still replaces the callback, as before.
DeepLinkHandler.cleanup() called Linking.removeAllListeners('url'),
which also removed the app's own 'url' listeners. It now removes only
its own subscription.
…led on 1.9.0 or earlier Versions up to 1.9.0 set the first-launch flag before sending the install report, so a failed report left the device with the flag set, no install ID, and every event dropped. Such a device now reports its install once after upgrading. It appears as an install dated at the upgrade, usually organic. An install now counts as reported when an install ID is stored or a new marker (@LinkForty:install_report_accepted) is set. The marker is set on any 2xx response, so a response without an install ID can't trigger a report on every launch. Failed reports are still retried, and devices that already have an install ID never report again. The first-launch flag is still written, so a downgraded app doesn't report again.
…y report A device recovering from a first-launch report that failed on 1.9.0 or earlier belongs to a user who has been using the app, so routing them like a fresh install would be wrong. The recovery report no longer calls onDeferredDeepLink, whether the install is attributed, organic or the report fails. First-launch reports, and their retries, still call it.
When a device upgraded from 1.9.0 or earlier reports the install it failed to report, any link the server matches comes from a fingerprint match made at upgrade time, not from the click that led to the install. The recovery report therefore no longer saves that link as install data (getInstallData() stays null) and no longer makes it the link credited for later events, whether the install is attributed or organic. It stores only the install ID and the reported markers. A link tap that opens the app still sets the credited link through the deep link path.
…y-and-types # Conflicts: # README.md # llms.txt
|
🎉 This PR is included in version 1.9.2 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds on #14 (README accuracy): this branch starts from
docs/readme-accuracy, so its first commit (456acda) is #14's. Merge #14 first; after that, this PR shows only its own eight commits.Fixes
A failed install report is now retried
The install-reported flag (
@linkforty:first_launch) was saved before the install report was sent. When the report failed (no connection, a server error, the app killed while it was in flight), it was never sent again:installIdstayednullfor good and everytrackEvent()was dropped with a warning.Now the flag is saved only once the server accepts the report (a 2xx response), so each later launch reports again until one succeeds. The key keeps its name, so installs already reported by earlier versions are not reported again. A 2xx response without an
installIdalso sets the flag, so it can't loop.onDeferredDeepLinkstill getsnullon a launch whose report fails, and the launch that later reports successfully calls it with that report's result.Devices stuck on 1.9.0 or earlier recover once
For the release notes: devices whose first-launch install report failed on 1.9.0 or earlier report their install once after upgrading. These appear as installs dated at the upgrade, usually organic, because the original click is normally outside the attribution window by then.
Those versions left such a device with the first-launch flag set and no install ID, so all its events were dropped.
@linkforty:install_report_acceptedis set. The marker is set on any 2xx response.installIdsets the marker, so it can't loop.onDeferredDeepLink, whether the install is attributed, organic or the report fails. The user has been using the app, so routing them like a fresh install would be wrong. First-launch reports, and their retries, still call it.getInstallData()staysnull) and is not credited for later events. A link the user taps to open the app is credited as usual, through the deep link path.navigationRefaccepts React Navigation 7 refsNavigationRouteLike.paramswasRecord<string, unknown>. React Navigation 7 types route params asReadonly<object | undefined>, which doesn't assign to that type, socreateNavigationContainerRef()(with no param list) failed with TS2322 asnavigationRef, withstricton or off. That broke the documented setup andexamples/AutoNavTracking.tsx.paramsis nowReadonly<object | undefined>, andsanitizeScreenParamsaccepts any value and ignores non-objects and arrays at runtime.Logging only with
debug, API key never loggedDeepLinkHandlerlogged URLs and resolved link data withconsole.log/console.warnon every link and cold start, whateverdebugwas set to. It now logs only withdebug(new optionalnew DeepLinkHandler({ debug }); the no-argument constructor still works).init()withdebuglogged the whole config, includingapiKey. The key is now[redacted].debug, only failures are logged, as before: a failed install report or event,init()called twice, and an event dropped for lack of an install ID. None of these logs include link, device or secret data.Peer dependencies
react-nativepeer goes from>=0.64.0to>=0.65.0.@react-native-async-storage/async-storage2.x declaresreact-native: ^0.0.0-0 || >=0.65 <1.0.@react-native-async-storage/async-storage(^2.2.0 || ^3.0.0) andreact-native-device-info(^15.0.0) move fromdependenciestopeerDependencies. Apps had to install both anyway, because autolinking only links native modules listed in the app's ownpackage.json. As regular dependencies they also allowed a second, nested copy. An app on AsyncStorage 3 (npmlatestis 3.1.1) got a nested 2.x for the SDK. That 2.x JS looks up theRNCAsyncStoragenative module, but 3.x registersRNAsyncStorage, so the SDK's storage was null at runtime. As a peer, the SDK uses the app's one copy.getItem/setItem/removeItembut dropsmultiRemove.clearData()now usesremoveItem, so the SDK only calls methods that exist in both versions. The test mock now has only those shared methods.Deep link listener
onDeepLink()call added anotherLinking'url'listener, so after a second call every link reached the callback twice. The handler now keeps the subscription fromLinking.addEventListenerand adds it once. CallingonDeepLink()again still replaces the callback, as before: only the newest one is called, once per link.DeepLinkHandler.cleanup()calledLinking.removeAllListeners('url'), which also removed the app's own'url'listeners. It now calls.remove()on its own subscription only.clearData()It now also removes the saved link hosts and fetches them again.
Public-repo hygiene
Removed ticket IDs, references to private notes, and internal product wording from source comments and JSDoc. Removed ticket IDs and a private-repository link from past CHANGELOG entries. The hosted service is now described generically in the README and llms.txt.
Not changed
deviceModelandappVersionare collected byFingerprintCollector.collect(), but onlygenerateQueryParams()uses them (it's public, and nothing inside the SDK calls it). They're not sent on install or resolve. Removing them would change public output, and sending them would change what install matching gets, so this PR leaves them as they are.clearData()keeps theLinkinglistener: the SDK stays initialized afterclearData()(init()can't run again), so removing the listener would stop deep links until a restart.Verification
npm test: 103 passed. There are 24 new tests: install retry after a network error and after a 503, no re-report once reported or after a 2xx without an id, deferred delivery on the retried launch, no logging withoutdebug, API key redaction,clearData()removing link hosts,sanitizeScreenParamswith React Navigation-typed and non-object params, one listener after repeatedonDeepLink()/initialize(), andcleanup()leaving the app's own listener, and recovery of a stuck device (reports once, retries after a failure, a 2xx withoutinstallIddoesn't loop, a device with an install ID never re-reports, no deferred callback on a recovery report, and a recovery report saving neither link data nor a credited link). Against the previous source, 18 of the new tests fail. The device-with-an-install-ID test passes on any version; it guards against re-reporting.npm run buildpasses. The builtdist/has no ticket IDs or internal references.examples/AutoNavTracking.tsxplus typed and untyped refs against@react-navigation/native7.1.33 andreact-native0.83.2:stricton and off.stricton.strictoff, one unrelated React Navigation error remains:Stack.Navigatorasks for anidprop.src/against@react-native-async-storage/async-storage3.1.1's type definitions: no errors.Release
semantic-release: the
fixanddocscommits make this a patch release (1.9.1). Please don't publish by hand.