JS Core Migration - #699
Merged
Merged
Conversation
index.ts now delegates method dispatch, per-platform wire resolution, and event demuxing to the shared js-core-plugin package via a new RNTransport (src/rn-transport.ts). callRpc/callRpcVoid/callRpcWithCallback no longer exist in this repo. PurchaseConnector/index.ts adds a stable barrel export so PurchaseConnector's internal layout can change without touching index.ts. Test files updated to match the new module-load-time RNTransport construction and normalized error/response shapes.
…to Kotlin Pure language conversion, no behavior change. Updates every doc/command/CI reference to the .java path to point at .kt instead (release-check, version-bump, release-versioning.md, native-android.md, promote-release.yml, release.yml).
executeRpc runs on rpcExecutor's background thread; an uncaught exception there terminates the whole process via Android's default uncaught-exception handler, and the JS promise never resolves either way. AppsFlyerRpcHandler is a vendored dependency we do not control, so wrap dispatchToNative and normalize any unexpected Exception into the standard error response instead of letting it escape.
example and demos/appsflyer-react-native-app bump kotlinVersion from 2.1.20 to 2.4.10. android/build.gradle (this plugin's own module) drops its independently-pinned kotlin-gradle-plugin classpath entry and kotlin-stdlib 1.7.10 pin in favor of inheriting the host app's Kotlin compiler plus a kotlin_stdlib_version default of 2.4.10 to match -- a second, independently-versioned Kotlin plugin here previously caused a compiled-with-incompatible-Kotlin-version failure (version skew, not a stale pin). NOTE: android/build.gradle also contains an unrelated hunk (adding src/main/common to sourceSets) that belongs with the purchase-connector reorg commit -- folded in here rather than run as a separate git add -p pass. Split with git add -p android/build.gradle if you want that hunk on the other commit.
…, drop redundant wrapper class Android: PCAppsFlyerPackage.java moves from excludeConnector to a shared common/ dir instead of being duplicated in includeConnector too (the includeConnector copy and MappedValidationResultListener.java -- a one-caller wrapper around the native PurchaseClient.ValidationResultListener -- are deleted; callers use the native interface directly). iOS: AFTransactionFetcher.swift and PCAppsFlyer.h/.m drop dead code, stale comments, an unused import, and an errorAsDictionary helper duplicated at its only two call sites.
…level deep link buffering New AppsFlyerAttribution.swift buffers continueUserActivity/handleOpen(url:) calls made from the host app's AppDelegate (cold-start Universal Link / custom-scheme open) until RNAppsFlyerImpl's start RPC has succeeded, avoiding the unconfigured-host failure mode and the nobody-listening drop documented in known-issues-kb.md. RNAppsFlyerImpl.executeRpc flips bridgeReady on a successful start response. App-side AppDelegates (example, demos/appsflyer-react-native-app, and the Expo config plugin's injected template) now route through AppsFlyerAttribution.shared instead of calling AppsFlyerLib.shared() directly for these two calls.
AppsFlyer.js: every call site now passes a single params object per the new SDK surface (init, enableDebug, registerConversionListener, registerDeepLinkListener, setCurrentDeviceLanguage). AFInit no longer returns unsubscribe functions (the new SDK does not provide unregister-by-reference); replaced with an AFCleanup export wired into HomeScreen's effect cleanup. Also fixes an Android cold-start deep link bug: the native SDK does not inspect the launch Intent until init() has actually completed, so getInitialURL's re-delivery via performDeepLinking now always runs after init resolves instead of only inside an Android-only branch that ran before init could finish. registerSessionReadyListener registration now bails early if init failed, avoiding an assert-crash. HomeScreen.js also updates its deep-link status check from the stale lowercase 'found' to the real 'FOUND' enum value.
Cart.js adds a Share and invite friends button using generateInviteLink + the Share API. New ResultModal.js displays the raw JSON payload of the last conversion-data/deep-link callback for on-device debugging; wired into HomeScreen.js in the previous commit.
…sion-ready timeout fallback Every call site updated to the single-params-object API. Adds startWhenSessionReady(), which wraps registerSessionReadyListener + start() in a Promise with a timeout fallback so a stalled native session-ready callback cannot hang the whole auto-run flow.
… js-core-plugin API
App.js's bootstrap effect never actually called init() -- a dead, unreachable arrow function above it was supposed to and never ran, so registerSessionReadyListener fired against whatever native state was left from a previous run. Replaced with one bootstrap() that awaits init(), registers listeners only after it resolves, and starts via a startWhenSessionReady() helper with a timeout fallback.
rpcCatalog.js was calling the pre-migration positional-arg API (setCustomerUserId('x'), logEvent('name', {}, true)) and importing the no-longer-exported AppsFlyerConsent class. Rewritten against the actual object-param signatures, including two platform-specific corrections the .d.ts does not encode: sendPushNotificationData is Android-only (ios: null in the real RPC map) and validateAndLogInAppPurchase's purchase field is a platform oneOf (iOS: transactionId, Android: purchaseToken).
… app Replaces a single brace-expansion override with per-major-version pins (1.x, 2.x, 5.x) so all resolved majors get the patched version instead of just one.
Resolves the QA log file via simctl get_app_container instead of find-ing across every container on disk -- orphaned containers from past runs could cause find | head -1 to return a stale container's log instead of the current install's.
Reflects index.ts's new single-file entry point (no separate index.js/index.d.ts), the AppsFlyerConsent class removal in favor of a plain setConsentData object, RNAppsFlyerConstants.kt path, and updated native SDK version numbers (Android 7.0.1, iOS AppsFlyerRPC 7.0.12, RN >=0.76 New Architecture requirement).
AGP 9.2.1 still pulls vulnerable bouncycastle/jdom2/jose4j transitives; force pinned patched versions in the buildscript classpath.
classpath had no version, relying on implicit resolution.
…a pooled lane The 6 register/unregister listener calls touch AppsFlyerRpcHandler's unsynchronized fields and need strict FIFO ordering; everything else is a stateless passthrough. Single-thread executor previously serialized all RPCs, so a slow start/logEvent call head-of-line-blocked fast ones behind it. Adds isListenerLifecycleCall + unit test covering the routing table.
…raw pass-through registerDeepLinkListener normalizes every payload on the merged onDeepLinkReceived/onDeepLinking channel and defaults a missing status field to NOT_FOUND (dist/appsflyer-sdk.js normalizeDeepLinkStatus), including legacy attribution-only payloads that never had a status. Not fixable from this repo -- js-core-plugin is a compiled dependency and index.ts has no interception point before normalization runs. Updated the three affected tests to assert the dependency's actual behavior and documented the root cause in known-issues-kb.md.
…() snippet RN_DeepLinkIntegrate.md and RN_EspIntegration.md still referenced the removed initSdk name. RN_PurchaseConnector.md's core-init example called start() standalone instead of inside registerSessionReadyListener, violating the start() contract -- copy-pasting it would silently drop attribution. RN_ExpoDeepLinkIntegration.md and RN_ExpoInstallation.md were missing the RN >= 0.76 / New Architecture prerequisite that is mandatory as of 7.0.0.
The `registerDeepLinkListener` call order relative to `init()` is now platform-split due to native SDK constraints: - **iOS**: Must be registered *after* `init()`. Calling it early triggers a one-shot deferred deep link (DDL) request against an unconfigured host, permanently breaking DDL for the app process. - **Android**: Must be registered *before* `init()`. The native SDK has no buffering for early DDL results; an unattached listener drops events permanently. Previously, incidental RN lifecycle timing masked this, but it was unreliable. All documentation (`RN_API.md`, `RN_Integration.md`, `RN_UnifiedDeepLink.md`, `bridge-patterns.md`, `known-issues-kb.md`) and sample applications have been updated with `Platform.OS` conditionals to reflect this critical behavior. Also includes minor CI workflow fixes to install plugin dependencies and comment cleanups in native modules.
pazlavi
approved these changes
Aug 13, 2026
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.
Migrate core JS SDK to @appsflyer-sdk/js-core-plugin
Summary
Replaces this repo's hand-maintained RPC dispatch, method-name resolution, and event-demuxing logic with the shared @appsflyer-sdk/js-core-plugin npm package. index.ts now constructs AppsFlyerSDK from that package with a thin RNTransport implementing its RpcTransport interface, instead of owning callRpc/callRpcVoid/callRpcWithCallback logic directly. Platform-specific glue (mediation-network wire-value resolution, setUserFbLoginId big-integer precision) stays in index.ts as documented overrides.
Why
Centralizes RPC dispatch/wire-contract logic in one shared package instead of duplicating it per-platform-SDK-wrapper, so wire-contract fixes and new RPC methods land once upstream instead of being re-implemented here.
What changed
Core migration
Deep-link listener registration order (native SDK misalignment)
CI
Demo apps / tooling
Docs
Breaking changes
None beyond what's already tracked for 7.0.0 in MIGRATION.md/CHANGELOG.md — this branch is an internal implementation swap (dispatch/wire logic → shared package), not a new public API surface change. Consuming apps' registerDeepLinkListener call sites should adopt the new platform-conditional order (see Docs/RN_UnifiedDeepLink.md).
Testing