Dev/delivery 124647/rpc implementation - #425
Conversation
fix(ios-e2e): Xcode 26 + Swift compat libs for PurchaseConnector 6.17.x
Keep release branch versions for SDK/version files (6.17.9 iOS, 6.17.6 Android, 2.1.2 PC). Keep master for README/docs. Merge both CHANGELOG entries. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…900-rc1 Release 6.17.900-rc1
….17.x/6.17.900-rc1 Revert "Release 6.17.900"
…900-rc1 Release 6.17.900-rc1
- docs/RPC-Implementation-Plan.md: full spec covering iOS and Android RPC bridge architecture, protocol definition, and phased rollout - plans/01-rpc-phase1-csharp-layer.md: detailed execution plan for the C# layer (AppsFlyerRPCClient, onRPCEvent handler, method routing, unit tests) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces per-platform native bindings with a unified JSON-RPC bridge (AppsFlyerRPC.xcframework on iOS, af-android-plugin-bridge on Android). Core changes: - AppsFlyer.cs: all SDK calls routed through AppsFlyerRPCClient; platform-split #if blocks reordered so UNITY_ANDROID is checked first (safe — mutually exclusive on real devices); setCurrentDeviceLanguage guarded iOS-only; setPhoneNumber Android no-op (bridge requires countryCode, public API does not expose it) - AppsFlyerRPCClient.cs: new IAppsFlyerRPCClient interface + DefaultInstance - AppsFlyerRPCBridge.java: Android RPC bridge implementation - AppsFlyerRPCWrapper.mm + AppsFlyerRPC.xcframework: iOS RPC bridge Tests: - Tests_Suite.cs: Android contract tests (6 new), iOS routing guards updated, platform exclusions validated; 67 tests total (61 iOS+shared, 6 Android) Docs: - Android-RPC-Mapping.md: plugin bridge → SDK API reference for Android - iOS-RPC-Mapping.md: AppsFlyerRPC → AppsFlyerLib method mapping for iOS - docs/RPC-Coverage.md: cross-platform RPC coverage matrix E2E validated locally on emulator/simulator — zero RPC parse errors on both platforms after fixing subscribeForDeepLink method name split. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the static xcframework from Assets/Plugins/iOS and repo root; add pod 'AppsFlyerRPC' 7.0.11 to AppsFlyerDependencies.xml so EDM4U resolves it from CocoaPods alongside AppsFlyerFramework. AppsFlyerRPCWrapper.mm is unchanged — the ObjC API surface is identical. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove af-android-plugin-bridge, af-android-sdk-base, and af-android-sdk-dev local AARs from unitywrapper/libs and test-app/Assets/Plugins/Android. unitywrapper/build.gradle: - implementation 'com.appsflyer:af-android-plugin-bridge:7.0.1' - compileOnly "com.appsflyer:af-android-sdk:$ANDROID_SDK_VERSION" (replaces sdk-base/dev local files) - removed flatDir repository AppsFlyerDependencies.xml: - added com.appsflyer:af-android-plugin-bridge:7.0.1 so EDM4U declares it for Unity consumers alongside af-android-sdk Also includes unit testing examples appended to iOS-RPC-Mapping.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove EmailCryptType and setPhoneNumber: P/Invoke bridge stubs — both APIs were dropped in AppsFlyerFramework 7.0.1; the RPC layer handles these calls. Simplify mainTemplate.gradle to only declare af-android-plugin-bridge:7.0.1 since it provides af-android-sdk transitively (no direct SDK dep needed). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace android_sdk_version/ios_sdk_version inputs with android_plugin_bridge_version and ios_rpc_version throughout the rc-release workflow, bump-version.sh, and ios-pod-install.sh. - rc-release.yml: new inputs for af-android-plugin-bridge and AppsFlyerRPC; verify step checks RPC coords in AppsFlyerDependencies.xml; Slack message shows RPC bridge versions - bump-version.sh: bumps af-android-plugin-bridge in deps XML, build.gradle, and mainTemplate.gradle; bumps AppsFlyerRPC in deps XML and ios-pod-install.sh; retains android_sdk_version for wrapper compileOnly dep - ios-pod-install.sh: reads AppsFlyerRPC version from AppsFlyerDependencies.xml and writes it into the Podfile instead of AppsFlyerFramework Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…wrapper.yml workflow_dispatch does not support a secrets: block; it inherits secrets from the repository/environment directly. Secrets were incorrectly duplicated under workflow_dispatch, causing an IDE validation error. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
af-android-plugin-bridge:7.0.1 transitively brings af-android-sdk:7.0.1. The compileOnly dep in gradle.properties must match; 6.17.6 was the old direct-SDK version and is no longer correct. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
af-android-plugin-bridge declares af-android-sdk as api, so sdk classes are available to the wrapper via the bridge alone. Verified by successful assembleRelease without the compileOnly dep. Removes ANDROID_SDK_VERSION from: gradle.properties, unitywrapper/build.gradle, bump-version.sh, publish-android-wrapper.sh, publish-android-wrapper.yml, and rc-release.yml. android_sdk_version is no longer an input anywhere. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Update PLUGIN_VERSION in AppsFlyerAndroidWrapper.java and VERSION_NAME in gradle.properties to 7.0.1 ahead of unity-wrapper Sonatype publish. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Method was missing from the wrapper causing NoSuchMethodError at runtime. af-android-plugin-bridge routes it through the RPC bridge to AppsFlyerLib. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Re-version to 7.0.11 to include the setPartnerData fix missing from 7.0.1. Also updates AppsFlyerDependencies.xml to reference unity-wrapper:7.0.11. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PurchaseConnector 7.0.0 pinned AppsFlyerFramework = 7.0.0, conflicting with AppsFlyerRPC 7.0.11 which requires AppsFlyerFramework = 7.0.1. Bumped PurchaseConnector to 7.0.1 (compatible with AppsFlyerFramework 7.0.1) and AppsFlyerFramework to 7.0.1. Also removed af-android-sdk:6.17.6 explicit declaration — it is a transitive dep via af-android-plugin-bridge:7.0.1 (api). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… billing v8 - purchase-connector 2.1.2 → 2.2.0 (billing library v8 support) - billingclient:billing 5.2.0 → 8.0.0 - unity-wrapper artifact version 7.0.11 → 7.0.12 - Remove af-android-sdk:6.17.6 explicit declaration (transitive via bridge) - AppsFlyerFramework 7.0.0 → 7.0.1, PurchaseConnector 7.0.0 → 7.0.1 in iOS pods Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nvoke AppsFlyerRPC 7.0.11 does not handle registerDeeplinkListener, so the deep link delegate was never set and onDeepLinking callbacks never fired. Fall back to instance.subscribeForDeepLink (P/Invoke _subscribeForDeepLink) on iOS which correctly sets AppsFlyerLib.deepLinkDelegate. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…own SDK lifecycle
AppsFlyerAndroid.initSDK called AppsFlyerAndroidWrapper.initSDK (which called
AppsFlyerLib.init with a conversion listener) AND then AppsFlyer.cs fired
ExecuteFire("init") through the RPC bridge, which called AppsFlyerLib.init again
overwriting the conversion listener. Result: onConversionDataFail("Launch exception: null").
Fix: AppsFlyerAndroid.initSDK now only wires the RPC bridge callback routing
(InitAndroidBridge). AppsFlyerLib.init is called exclusively by the RPC bridge.
Similarly, startSDK no longer calls instance.startSDK on Android — the RPC
bridge owns AppsFlyerLib.start. iOS keeps both paths (deprecated _startSDK
is a no-op on the native side).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
startSDK no longer calls instance.startSDK() on Android (removed to fix
double AppsFlyerLib.init). The shared test must verify the RPC path
(ExecuteFire("start")) which fires on all platforms, not the iOS-only
native bridge call.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nSessionReady via RPC Both iOS and Android were firing onSessionReady synthetically instead of waiting for the native SDK callback. Collapsed to a single ExecuteFire call on both platforms; removed dead _nativeRegisterSessionReadyListener P/Invoke on iOS. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Required by rc-release.yml validation: grep -q "kAppsFlyerPluginVersion = \"$PLUGIN_VERSION\"". Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
getConversionData() fires ExecuteFire("registerConversionListener") which is
required for onInstallConversionData to reach Unity. E2E phase_1 was failing
because the listener was never registered.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| /// but this is no longer a blocking unknown. | ||
| /// </summary> | ||
| public static event EventHandler OnRequestResponse | ||
| public static async Awaitable<AFSDKValidateAndLogResult> validateAndLogInAppPurchase(AFPurchaseDetailsAndroid details, Dictionary<string, string> additionalParameters) |
There was a problem hiding this comment.
validateAndLogInAppPurchase is split into two method overloads distinguished only by parameter type — (AFPurchaseDetailsAndroid details, ...) (line 960) and (AFSDKPurchaseDetailsIOS details, ...) (line 982) — each independently gated by its own #if UNITY_ANDROID / #if UNITY_IOS || UNITY_STANDALONE_OSX block, and each returning null on the "wrong" platform. This is overloading standing in for what should be a single abstraction: AFPurchaseDetailsAndroid and AFSDKPurchaseDetailsIOS describe the same domain concept (a purchase to validate) but share no common type, so adding a third platform means adding a third overload rather than a third implementation of an existing contract — a SOLID (Open/Closed) violation. AFSDKValidateAndLogResult (the shared return type both overloads already converge on via QueryValidateAndLogAsync) should likewise be defined against an interface rather than as a single concrete class, so the result contract is explicit and not just an accident of both overloads happening to call the same private helper today.
Recommend: introduce a common purchase-details interface (e.g. IAFPurchaseDetails) implemented by AFPurchaseDetailsAndroid and AFSDKPurchaseDetailsIOS, and collapse the two overloads into one validateAndLogInAppPurchase(IAFPurchaseDetails details, Dictionary<string, string> additionalParameters) that dispatches to the platform-specific payload-building logic internally (or via a small per-platform strategy), rather than via overload resolution on the caller's static type. Define AFSDKValidateAndLogResult against a corresponding result interface for the same reason.
References:
…eak, PII log redaction, purchase-validation interface refactor - FireAsync now catches/logs RPC exceptions so non-awaited callers aren't silently swallowed - Fix stale getAppsFlyerUID doc comment (matches isSessionReady/getSdkVersion wording) - unregisterConversionListener/unregisterDeeplinkListener null out static callback fields - Rename generateInviteLinkAsync -> generateInviteLink; catch AppsFlyerRPCException before generic Exception - AppsFlyerRPCBridge.kt: stop logging full RPC request payload (PII/revenue data) on dropped fire-and-forget calls - Collapse validateAndLogInAppPurchase Android/iOS overloads into one method via new IAFPurchaseDetails/IAFValidateAndLogResult interfaces Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…lowing - test-app: add AppsFlyerLifecycleNudgeActivity, a momentary translucent Activity that forces a synthetic pause/resume on AppsFlyerUnityActivity right after registerSessionReadyListener(). AppsFlyerLib's own ActivityLifecycleCallbacks are only registered once Unity's managed layer boots (via init()), which is always a beat behind Android's real, launch-triggering onResume() - without this nudge, session readiness/ start() would stay unevaluated until the user genuinely backgrounds and foregrounds the app. No AppsFlyerLib/RPC API is called by the nudge itself; iOS is unaffected (its SDK re-evaluates readiness immediately if already active at listener-registration time). - AppsFlyer.cs: FireAsync now rethrows after logging, so awaited callers still see RPC failures instead of having them silently swallowed alongside the non-awaited-caller logging fix from the prior review pass. - AppsFlyerDependencies.xml: drop the unity-wrapper Maven coordinate (no published version matches this repo's current Kotlin AppsFlyerRPCBridge - every published version calls AppsFlyerRpcHandler's constructor with a raw Context where af-android-plugin-bridge:7.0.12 now expects a Function0<Context> supplier); add iOS Swift Package Manager entries for AppsFlyerLib-Dynamic/AppsFlyerRPC/PurchaseConnector-Dynamic. - test-app: vendor a locally-built unity-wrapper .aar directly under Assets/Plugins/Android pending a published fix, and sync mainTemplate.gradle to match. - Add missing .meta files for IAFPurchaseDetails.cs/IAFValidateAndLogResult.cs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ady API - README/Installation: require Unity 2023.1+ (raised from 2019.4, for Awaitable/Awaitable<T>) and EDM4U 1.2.187+ (earlier 1.2.x mis-resolves the new iOS Swift Package Manager AppsFlyerRPC dependency). Add a breaking-changes table for the 7.x RPC-bridge rename/async migration (initSDK/startSDK/stopSDK/isSDKStopped/getAppsFlyerId -> init/start/stop/isStoppedAsync/getAppsFlyerUIDAsync). - API.md/BasicIntegration.md: bring in line with the current AppsFlyer.cs surface (Awaitable *Async twins, no more initSDK/startSDK), and add a Session Ready Listener section covering registerSessionReadyListener(), OnSessionReady, isSessionReady()/Async(), and the Android cold-launch caveat addressed by the lifecycle-nudge fix. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Earlier 1.2.x releases (e.g. 1.2.183) mis-resolve this plugin's iOS Swift Package Manager dependencies (AppsFlyerRPC, introduced with the RPC bridge migration) - see docs/Installation.md#requirements. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
EDM4U links AppsFlyerLib-Dynamic/PurchaseConnector-Dynamic into UnityFramework's Frameworks phase but never embeds the resulting dynamic frameworks into the app bundle, causing a dyld "Library not loaded" crash at launch. Embeds them by their real framework binary name via the public PBXProject API, the same way Unity embeds UnityFramework.framework/UnityRuntime.framework. AppsFlyerRPC is a static xcframework and needs neither linking help nor embedding. In the test app, wire up the existing (but previously unused) native ATT prompt and fix its ordering relative to start(): the SDK's first session send was firing before the user answered the tracking prompt, so it could never carry IDFA even on a grant. start() now waits for the native ATT completion callback (with a timeout fallback), replacing waitForATTUserAuthorizationWithTimeoutInterval, which no longer exists in this plugin version.
| /// <param name="eventName">Event Name as String.</param> | ||
| /// <param name="eventValues">Event Values as Dictionary.</param> | ||
| public static void sendEvent(string eventName, Dictionary<string, string> eventValues) | ||
| /// <summary>Starts the SDK. A session is sent immediately, and on every foreground transition.</summary> |
There was a problem hiding this comment.
Shipping both a synchronous and an *Async twin for the same underlying RPC call (isSessionReady()/isSessionReadyAsync() here, and the same pattern repeated for getSdkVersion, getAppsFlyerUID, getOutOfStore, isPreInstalledApp, getAttributionId, getHostName, getHostPrefix, isStopped) is redundant API surface — every one of these pairs hits the same native call, just via Query (blocking) vs QueryAsync (background-thread hop). Recommend converging on a single pattern without an Async suffix in the method name (i.e. every getter is simply awaitable), rather than maintaining two names, two doc comments, and two sets of tests per getter going forward.
| { | ||
| if (text.Contains("/* " + frameworkFileName + " in Embed Frameworks */")) continue; | ||
|
|
||
| string fileGuid = proj.AddFile(frameworkFileName, "Frameworks/" + frameworkFileName, PBXSourceTree.Build); |
There was a problem hiding this comment.
This is the PR's headline iOS fix (resolving a "Library not loaded" dyld crash by embedding AppsFlyerLib.framework/PurchaseConnector.framework into the app bundle), but the embed step references the file at "Frameworks/" + frameworkFileName relative to BUILT_PRODUCTS_DIR. The surrounding comment justifies this by analogy to how Unity embeds UnityFramework.framework — but that framework is a genuine Xcode target product built directly into BUILT_PRODUCTS_DIR, whereas AppsFlyerLib.framework/PurchaseConnector.framework here are Swift Package Manager package library products consumed by the main target. Xcode's build system places package-library framework products under BUILT_PRODUCTS_DIR/PackageFrameworks/, not BUILT_PRODUCTS_DIR/Frameworks/ — a well-documented SPM/Xcode integration gotcha (see the referenced Firebase issue). If that's the case here, this embed step silently references a file that doesn't exist at the assumed path, and the exact crash this PR sets out to fix would persist (or the build fails outright with a missing-file error).
This needs to be verified against an actual Xcode build (inspect the generated project's DerivedData/.../Build/Products/<config>-iphoneos/ output) before merge — it's the difference between this PR's central fix working or not.
References:
| PBXProject proj = new PBXProject(); | ||
| proj.ReadFromFile(projPath); | ||
| string mainTarget = proj.GetUnityMainTargetGuid(); | ||
| proj.AddCopyFilesBuildPhaseBeforeTargetPostprocess(mainTarget, "Embed Frameworks", "", "10"); |
There was a problem hiding this comment.
AddCopyFilesBuildPhaseBeforeTargetPostprocess(mainTarget, "Embed Frameworks", "", "10") unconditionally creates a new copy-files build phase in memory every run, before the per-file idempotency check (the raw-text scan a few lines below) has a chance to decide anything. It's currently masked because the write (proj.WriteToFile) is skipped whenever changed stays false, but there's no lookup-by-name for an existing "Embed Frameworks" phase — on any build where at least one framework still needs embedding (first build, or a project regenerated fresh), and on any future build with partially-persisted state from an earlier incremental build, this can end up persisting a second, duplicate "Embed Frameworks" phase. Two same-named copy-files phases targeting overlapping content is a known source of Xcode "Multiple commands produce..." build failures.
Recommend looking up an existing "Embed Frameworks" PBXCopyFilesBuildPhase by name first (via proj.GetAllBuildPhasesForTarget or similar) and reusing it, rather than assuming none exists.
References:
| 2. Scroll down and select `Privacy - Tracking Usage Description`. | ||
| 3. Add as the value the wording you want to present to the user when asking for permission to collect the IDFA. | ||
| 3. Call the `waitForATTUserAuthorizationWithTimeoutInterval` api before `startSDK()` | ||
| 3. Call the `waitForATTUserAuthorizationWithTimeoutInterval` api before `start()` |
There was a problem hiding this comment.
This step (and its code sample at line 139 calling AppsFlyer.waitForATTUserAuthorizationWithTimeoutInterval(60)) documents an API that no longer exists — confirmed via Assets/AppsFlyer/Tests/Tests_Suite.cs:566, which asserts typeof(AppsFlyer).GetMethod("waitForATTUserAuthorizationWithTimeoutInterval") is null with the comment "confirmed out of scope." The sample app's own new code (QATestScript.cs:164) says outright: "This replaces waitForATTUserAuthorizationWithTimeoutInterval, which no longer exists in this plugin version... despite still being documented." A developer following this guide gets a compile error. docs/API.md:1494-1507 has the identical stale entry, and the removal isn't listed in CHANGELOG.md's breaking-changes bullet either.
Recommend replacing this step with guidance to request ATT authorization directly (e.g. via a small native call) and pointing to QATestScript.cs's RequestATTThenStart() as a reference implementation, removing the stale docs/API.md entry, and adding the removal to CHANGELOG.md's breaking-changes list.
References:
|
|
||
| ### isSDKStopped | ||
| **`bool isSDKStopped()`** | ||
| ### isStopped |
There was a problem hiding this comment.
This section documents only isStoppedAsync(), omitting the synchronous isStopped() twin that every other getter on this page documents alongside its *Async counterpart. More importantly, it omits a real platform limitation visible in the implementation's own doc comment (AppsFlyer.cs:888-889): there is no iOS RPC method for isStopped at all, so on iOS this always returns false regardless of actual SDK state — a silent wrong-answer risk for any integrator who checks this on iOS expecting it to reflect stop().
Note: AppsFlyer.cs:134 (checked, pending resolution) recommends collapsing the isStopped()/isStoppedAsync() sync/*Async duplication — and the same pattern across getSdkVersion, getAppsFlyerUID, isSessionReady, getOutOfStore, isPreInstalledApp, getAttributionId, getHostName, getHostPrefix — down to a single awaitable method per getter. If that's adopted, this doc fix applies to whichever single method survives rather than to an *Async-suffixed twin specifically; the missing-sync-signature half of this finding becomes moot, but the iOS-only caveat still needs documenting either way.
Recommend documenting both signatures together for now (matching the page's own convention) and adding an explicit "Android only — always returns false on iOS" note, revisiting once the sync/async consolidation in AppsFlyer.cs:134 is decided.
References:
|
Inline comment on
Recommend removing References: |
…ation Addresses PR review comment: isStopped section was missing the sync signature (unlike every other getter on the page) and the platform limitation already noted in AppsFlyer.cs's own doc comment.
…ethods Consistent with this major release's no-backward-compatible-aliases policy for the other renamed APIs. Docs and the QA test app now use the new AppsFlyerConsent(...) constructor directly.
…erences That API was removed during the RPC migration (Tests_Suite.cs:566-567 asserts it no longer exists) but BasicIntegration.md and API.md still documented and called it, which would fail to compile. Point to QATestScript.cs's RequestATTThenStart() as the reference implementation instead, and add the removal to CHANGELOG.md's breaking-changes list.
…andler handleCollectDataFromLauncherActivity casts the provided context to Activity and errors (422) if it isn't one, so collectDataFromLauncherActivity() was always failing on Android. Confirmed via decompiling af-android-plugin-bridge that the handler's own cached appContext already derives applicationContext internally before caching, so returning the raw Activity here doesn't reintroduce the leak the prior comment was guarding against.
The prefab (AppsFlyerObjectScript.cs, AppsFlyerObjectEditor.cs, its logo asset) is removed in favor of manual integration only. Nothing in the plugin itself depended on it - it was purely an optional convenience wrapper around AppsFlyer.init()/start(). Docs updated: BasicIntegration.md's dedicated prefab section and MacOS-initialization step are replaced with pointers to manual integration; other docs' sample code (previously modeled on extending AppsFlyerObjectScript) is renamed to a plain AppsFlyerInit class name so it no longer implies the removed shipped script. Breaking change noted in CHANGELOG.md and README.md's 7.x.x breaking-changes section.
…ample Both receipt-validation code samples still called the pre-7.0.1 initSDK()/startSDK() APIs, which were renamed to init()/start() and made async Awaitable with no backward-compatible alias.
…erences These APIs were renamed to init/start/stop/getAppsFlyerUIDAsync (all now async Awaitable) in the 7.0.1 RPC migration with no backward-compatible alias, but several doc samples across DMAConsent.md, InAppEvents.md, MigrationGuide.md, UnifiedDeepLink.md, UninstallMeasurement.md, UserInvite.md, conversion-data-unity.md, and purchase-connector.md still called the old names - copy-pasting them would fail to compile. Left untouched: docs/API.md's 'Renamed from X' notes, RPC-Coverage.md/RPC-Implementation-Plan.md's historical mapping tables, and README.md/Introduction.md's own breaking-changes announcements, since those intentionally reference the old names for historical/migration context.
PBXProject.FindFileGuidByProjectPath and AddFileToEmbedFrameworks are both documented as no-ops when the file/embed entry already exists, so the idempotency guard no longer needs to scan the raw .pbxproj text for a comment string that happened to match Xcode's current output format.
isSessionReady(), getSdkVersion(), getAppsFlyerUID(), getOutOfStore(), isPreInstalledApp(), getAttributionId(), getHostName(), getHostPrefix(), and isStopped() blocked the calling thread (up to 5s on iOS native lag) and duplicated their already-existing *Async twins. Removed the sync forms; *Async is now the only public API for these. Updated Tests_Suite.cs (dropped the now-obsolete sync-only tests), docs/API.md, docs/BasicIntegration.md, README.md, and the sample app's AppsFlyerAPITester.cs/QATestScript.cs to call the *Async APIs. AppsFlyerAPITester's Call delegate is now Func<ApiEntry, Awaitable>, properly awaited before logging/displaying results, since firing an async lambda without awaiting it logged the "OK" fallback before the real result was ever assigned. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nment Removes the sync/async duplicate getter pattern entirely and renames the remaining *Async methods (getSdkVersion, getAppsFlyerUID, isSessionReady, isStopped, getOutOfStore, isPreInstalledApp, getAttributionId, getHostName, getHostPrefix) to drop the Async suffix, matching the RPC schema's canonical names and the rest of the async-by-default public API (init/start/etc). Updates test method names, the sample app's API tester and QA script to use the new names, and syncs README/CHANGELOG/API/BasicIntegration/MigrationGuide docs accordingly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…art/logEvent Suppresses CS1998 for AppsFlyer.cs, where many methods are #if UNITY_ANDROID/ UNITY_IOS-guarded and legitimately have no await on the other platform, and makes the two Obsolete sharing-filter wrappers' fire-and-forget dispatch explicit with a discard to fix CS4014. Adds the RPC schema's awaitResponse parameter (already present on start and logEvent) to their C# signatures, routing through the blocking Execute() path when true so callers can wait for the actual server round trip instead of just the fire-and-forget dispatch to native - matching the pattern already available in the react-native/flutter plugins. Defaults to false, preserving existing fire-and-forget behavior for all current call sites. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
BuildScript.cs: drop obsolete AndroidArchitecture.X86_64 (no longer supported) and switch SetApplicationIdentifier(BuildTargetGroup, string) to the non-obsolete NamedBuildTarget overload for both Android and iOS. QATestScript.cs: suppress the CS0414 unused-field warning on _attDetermined, which is only read inside the UNITY_IOS && !UNITY_EDITOR branch of RequestATTThenStart() and so is genuinely write-only on Editor/ Android builds. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ester setSharingFilterForAllPartners()/setSharingFilter() are exercised intentionally for QA coverage despite being marked Obsolete, and the local AsAwaitable() helper genuinely has no await by design (it wraps a synchronous void call to fit the Func<ApiEntry, Awaitable> shape). Suppress CS0618/CS1998 at each site instead of leaving unexplained warnings in the build log. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…lter Both were Obsolete-marked thin wrappers around setSharingFilterForPartners with no remaining callers worth keeping around. Removes them and their sole call sites in the sample app's API tester (along with the now-unused AsAwaitable() helper that existed only to adapt their void signature), and updates docs/API.md, docs/RPC-Coverage.md, and CHANGELOG.md accordingly. Also fixes setSharingFilterForPartners' doc, which was stale (void instead of async Awaitable). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Also fixes package.json's stale "unity": "2019.4" min-version field (should have been 2023.1 since Awaitable support was added) and syncs its "version" field, which had drifted to 6.17.900.
Summary
Migrates the AppsFlyer Unity plugin's core API from the legacy AndroidJavaClass/DllImport native bridges to a unified, schema-driven RPC transport. Every public method and parameter is now aligned to a canonical JSON schema (appsflyer-plugins-rpc-schema.json) shared across platforms, replacing ad-hoc per-platform bridging code with a single dispatch path (AppsFlyerRPCClient → Fire/Query).
What changed
C# plugin API (Assets/AppsFlyer/AppsFlyer.cs)
Android native bridge
iOS native bridge
Sample/test app
Removed
Out of scope / explicitly excluded
Testing