From 8fe4d1b58f5889e431f66cbdead55a32796281b3 Mon Sep 17 00:00:00 2001 From: "kobi.kagan" Date: Sun, 26 Jul 2026 11:47:19 +0300 Subject: [PATCH] Correct SPM + Purchase Connector guidance: mutually exclusive, not mixable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Investigation (prompted by a question on whether PC-via-CocoaPods and Core-via-SPM can coexist) found CI evidence that Flutter's tooling may silently drop the CocoaPods PurchaseConnector integration once it decides a plugin is SPM-eligible, even when the app's Podfile still sets $AppsFlyerPurchaseConnector = true. This was not conclusively resolved either way, so rather than continuing to investigate, doc/Installation.md and doc/PurchaseConnector.md now state plainly: apps using Purchase Connector must not enable SPM for this plugin at all — use CocoaPods for both Core and Purchase Connector. Apps that don't use Purchase Connector can use SPM freely. F-060 updated to match: the "hybrid" E2E run is now described accurately (no link crash, but does not demonstrate PurchaseConnector functions in that configuration) rather than as a validated supported path. Co-Authored-By: Claude Sonnet 5 --- doc/Installation.md | 2 +- doc/PurchaseConnector.md | 2 +- .../features/F-060-swift-package-manager-support.md | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/Installation.md b/doc/Installation.md index 100c7aa..289fab9 100644 --- a/doc/Installation.md +++ b/doc/Installation.md @@ -15,7 +15,7 @@ This will download the AppsFlyer flutter plugin to your project, you may observe Starting with v6.18.0, the plugin's **Core** integration supports Swift Package Manager on iOS, alongside continued full CocoaPods support. If your app has SPM enabled (the default on Flutter 3.44+, or via `flutter config --enable-swift-package-manager` on Flutter 3.24+), no extra setup is needed — Flutter's tooling picks up the plugin's `Package.swift` automatically. -**Purchase Connector is CocoaPods-only.** If your app uses the [Purchase Connector](PurchaseConnector.md), it must stay on CocoaPods for now — there is no SPM opt-in path for it yet, pending resolution of an upstream Flutter limitation ([flutter/flutter#161182](https://github.com/flutter/flutter/issues/161182)). Apps that need both SPM (for Core) and Purchase Connector (via CocoaPods) can use both simultaneously; Flutter's tooling handles this automatically as long as your `Podfile` still exists. +**If you use Purchase Connector, do not enable SPM for this plugin.** [Purchase Connector](PurchaseConnector.md) requires CocoaPods for the entire plugin (Core included) — it cannot currently be combined with SPM, pending resolution of an upstream Flutter limitation ([flutter/flutter#161182](https://github.com/flutter/flutter/issues/161182)). SPM is recommended only for apps that don't use Purchase Connector at all; if you don't, keep CocoaPods and the `$AppsFlyerPurchaseConnector` Podfile flag as documented in [PurchaseConnector.md](PurchaseConnector.md). --- ## Huawei Referrer diff --git a/doc/PurchaseConnector.md b/doc/PurchaseConnector.md index adc72ab..9cab757 100644 --- a/doc/PurchaseConnector.md +++ b/doc/PurchaseConnector.md @@ -81,7 +81,7 @@ appsflyer.enable_purchase_connector=true ``` Once you set these properties, the Purchase Validation feature will be integrated into your project and you can utilize its functionality in your app. -> ⚠️ **iOS + Swift Package Manager**: Purchase Connector requires **CocoaPods** — there is no Swift Package Manager opt-in path for it. This is a temporary limitation pending an upstream Flutter fix ([flutter/flutter#161182](https://github.com/flutter/flutter/issues/161182)). If your app has adopted SPM for the Core integration (see [Installation.md](Installation.md#ios-swift-package-manager-spm-support)), keep your `Podfile` in place and set `$AppsFlyerPurchaseConnector = true` there as above — Flutter's tooling will use SPM for Core and CocoaPods for Purchase Connector at the same time. If you skip the Podfile entirely (SPM-only), calling any Purchase Connector API will silently fail with a `MissingPluginException` — see the next section. +> ⚠️ **iOS + Swift Package Manager**: Purchase Connector requires **CocoaPods for the entire plugin** — there is no Swift Package Manager path for it, and it cannot currently be combined with Swift Package Manager for the Core integration either. This is a temporary limitation pending an upstream Flutter fix ([flutter/flutter#161182](https://github.com/flutter/flutter/issues/161182)). **If your app uses Purchase Connector, do not enable Swift Package Manager for this plugin — keep your `Podfile` and use CocoaPods for both Core and Purchase Connector.** If you enable SPM anyway, calling any Purchase Connector API will silently fail with a `MissingPluginException` — see the next section. SPM is only recommended for apps that don't use Purchase Connector at all (see [Installation.md](Installation.md#ios-swift-package-manager-spm-support)). ### What Happens if You Use Dart Files Without Opting In? diff --git a/internal-docs/features/F-060-swift-package-manager-support.md b/internal-docs/features/F-060-swift-package-manager-support.md index 5ba4ddd..617fb98 100644 --- a/internal-docs/features/F-060-swift-package-manager-support.md +++ b/internal-docs/features/F-060-swift-package-manager-support.md @@ -76,20 +76,20 @@ No dedicated automated test — this is a build-configuration/distribution-mecha - `swift package describe` — genuine dependency resolution against the live `AppsFlyerFramework` GitHub repository, confirming the manifest resolves product `AppsFlyerLib` at `Exact: 6.18.0` (corrected from an earlier `from:` range pin during review — see Known Limitations) and picks up all 3 Core `.m` sources correctly. - `pod spec lint --quick --allow-warnings` — passed, confirming the podspec's repointed `source_files`/`public_header_files` globs resolve correctly against the moved tree. - `flutter test test` — all 38 existing Dart tests pass unaffected (this change touches only iOS native file locations and build manifests, not Dart code). -- **Real-device iOS E2E, dispatched via GitHub Actions with real credentials, 3 of the tech design's 4 combinations — all 6 scenario phases PASS in each:** - - SPM, Core only, `.exact("6.18.0")` pin — [run 30191649705](https://github.com/AppsFlyerSDK/appsflyer-flutter-plugin/actions/runs/30191649705). `getSDKVersion` confirmed resolving `6.18.0`, not a drifted patch release (an earlier run against the pre-fix `from:` pin had resolved `6.18.1` — see Known Limitations). - - Hybrid: SPM Core + CocoaPods PurchaseConnector simultaneously (realistic config for an app that wants both) — [run 29848672331](https://github.com/AppsFlyerSDK/appsflyer-flutter-plugin/actions/runs/29848672331). - - Pure CocoaPods, Core + PurchaseConnector, SPM explicitly disabled — [run 29901950273](https://github.com/AppsFlyerSDK/appsflyer-flutter-plugin/actions/runs/29901950273). +- **Real-device iOS E2E, dispatched via GitHub Actions with real credentials — all 6 scenario phases PASS in each:** + - SPM, Core only, `.exact("6.18.0")` pin — [run 30191649705](https://github.com/AppsFlyerSDK/appsflyer-flutter-plugin/actions/runs/30191649705). `getSDKVersion` confirmed resolving `6.18.0`, not a drifted patch release (an earlier run against the pre-fix `from:` pin had resolved `6.18.1` — see Known Limitations). **This is the only SPM configuration we recommend or support.** + - Pure CocoaPods, Core + PurchaseConnector, SPM explicitly disabled — [run 29901950273](https://github.com/AppsFlyerSDK/appsflyer-flutter-plugin/actions/runs/29901950273). **This is the only supported configuration for apps using Purchase Connector.** + - SPM Core + CocoaPods PurchaseConnector configured simultaneously — [run 29848672331](https://github.com/AppsFlyerSDK/appsflyer-flutter-plugin/actions/runs/29848672331). This run only demonstrates the app builds and links without a crash when both are configured at once (no duplicate-symbol failure) — it does **not** demonstrate Purchase Connector actually functions in this configuration, and CI's own logs suggest Flutter's tooling may silently drop the CocoaPods `PurchaseConnector` pod entirely once it detects the plugin has a `Package.swift`. **This combination is explicitly not supported or recommended** — see doc/Installation.md and doc/PurchaseConnector.md, both updated to state that apps using Purchase Connector must not enable SPM for this plugin at all. -> **Remaining gap**: the 4th combination — actively calling a Purchase Connector API from an SPM-only integration and confirming it raises `MissingPluginException` rather than crashing or hanging — has not been exercised by a real test, only reasoned through statically (see the tech design's corrected failure-mode analysis). This is a low-risk, non-blocking gap: the mechanism (`ENABLE_PURCHASE_CONNECTOR` never defined under SPM) is the same one already exercised today by the CocoaPods not-opted-in path, just reached a third way. +> **Remaining gap**: whether SPM+PurchaseConnector configured simultaneously silently drops Purchase Connector or actively fails with `MissingPluginException` has not been conclusively confirmed either way — investigation was inconclusive (see above) and this combination is now explicitly unsupported regardless of the answer, so it was not pursued further. --- ## Known Limitations - **Purchase Connector is not available via SPM this release, with no opt-in mechanism at all.** `Package.swift` never references `ios/PurchaseConnector/` and has no equivalent of the podspec's `pod_target_xcconfig` macro injection, so `ENABLE_PURCHASE_CONNECTOR` is never defined for an SPM build under any configuration. Calling a Purchase Connector Dart API from an SPM-only integration fails with the same generic Flutter `MissingPluginException` that F-054 already documents for the CocoaPods not-opted-in case — this is not a new or worse failure mode, but it is a third, permanent path to it (not something a developer can fix by setting a flag, unlike the other two paths). Apps that need Purchase Connector must stay on CocoaPods until flutter/flutter#161182 is resolved. +- **SPM and Purchase Connector cannot be combined, even though nothing prevents an app from *configuring* both at once.** An app can set `$AppsFlyerPurchaseConnector = true` in its Podfile while also having SPM enabled — this doesn't crash or error at build time (verified: [run 29848672331](https://github.com/AppsFlyerSDK/appsflyer-flutter-plugin/actions/runs/29848672331) built and linked successfully) — but CI's logs suggest Flutter's tooling may silently drop the CocoaPods `PurchaseConnector` pod once it decides the plugin is SPM-eligible, meaning the feature may silently not be present despite looking configured. This was not conclusively resolved either way; instead of continuing to investigate, this combination is explicitly documented as unsupported (`doc/Installation.md`, `doc/PurchaseConnector.md`): **apps using Purchase Connector must not enable SPM for this plugin at all.** - **flutter/flutter#161182 (Flutter's own plugin tooling lacking conditional-compilation support under SPM) is the real blocker**, not a SwiftPM limitation — investigated during research (`internal-docs/researches/R-001-spm-support.md`), including whether SwiftPM Package Traits (Swift tools 6.1+) could work around it. They cannot: the issue's own text states Flutter would need to add trait support to its plugin tooling first, which it has not. - **Three architectural alternatives to bring Purchase Connector onto SPM were evaluated and rejected for this release** (see `internal-docs/researches/R-001-spm-support.md` addendum): a second product in the same `Package.swift` (not viable — Flutter's tooling only links one product per plugin, no documented support for a second), an environment-variable-gated compile flag (technically usable but fragile — requires every consuming app to set an env var on every build/CI run with silent failure if forgotten), and splitting Purchase Connector into its own federated pub.dev package (architecturally sound, no hidden blocker, but a separate, larger initiative with its own versioning/release pipeline — a candidate future initiative, not part of this ticket). -- **Real-device build verification is outstanding** — see Tests section above. Static/network verification (Swift manifest resolution, podspec lint, Dart test suite) passed, but the tech design's full 4-path device build has not yet run. ---