-
Notifications
You must be signed in to change notification settings - Fork 59
Add Swift Package Manager (SPM) support for Flutter-SDK #356
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat/release_4.2.0
Are you sure you want to change the base?
Changes from all commits
b3d30e8
2a7a415
5d96862
203c1e3
b91d176
c0b7ecf
dfd2cf9
52d2d8e
a7c16ee
8751ec8
9e9fdf6
74ecaa9
c22b0dc
8230f92
8922ed1
7fa5ea4
25a953a
c021559
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,124 @@ | ||||||||
| ## 📦 iOS Integration via Swift Package Manager (SPM) | ||||||||
|
|
||||||||
| Flutter introduced Swift Package Manager support as an **opt-in** feature in Flutter 3.24, and it became the **default** package manager in Flutter 3.44. The CleverTap Flutter plugin from v4.2.0 ships a `Package.swift` alongside its podspec, so both package managers resolve the same native SDK version (`CleverTap-iOS-SDK 7.8.1`). | ||||||||
|
|
||||||||
| > **Minimum requirements for SPM integration** | ||||||||
| > - Flutter 3.24+ (opt-in) - enabled by default from Flutter 3.44 | ||||||||
| > - Xcode 15+ (required for `swift-tools-version: 5.9`) | ||||||||
| > - iOS deployment target: 13.0+ | ||||||||
| > | ||||||||
| > If you are on Flutter < 3.24, the plugin continues to work via CocoaPods - no action required. | ||||||||
|
|
||||||||
| --- | ||||||||
|
|
||||||||
| ### How it works | ||||||||
|
|
||||||||
| The plugin ships a Swift package that pins `CleverTap-iOS-SDK 7.8.1`. When your app is built with SPM enabled, Flutter resolves it automatically - you don't add anything to your app's own `Package.swift` or edit any plugin files. | ||||||||
|
|
||||||||
| --- | ||||||||
|
|
||||||||
| ### Enabling SPM in your Flutter app | ||||||||
|
|
||||||||
| SPM is opt-in on Flutter 3.24–3.43 (and on by default from 3.44). There is **no** `flutter run`/`flutter build` flag and **no** `Info.plist` key for this - you enable it through Flutter configuration. | ||||||||
|
|
||||||||
| **Per project (recommended for testing)** - add to your app's `pubspec.yaml`: | ||||||||
|
|
||||||||
| ```yaml | ||||||||
| flutter: | ||||||||
| config: | ||||||||
| enable-swift-package-manager: true | ||||||||
| ``` | ||||||||
|
|
||||||||
| **Globally for your machine:** | ||||||||
|
|
||||||||
| ```bash | ||||||||
| flutter config --enable-swift-package-manager | ||||||||
| ``` | ||||||||
|
|
||||||||
| To turn it back off, use `enable-swift-package-manager: false` in `pubspec.yaml`, or `flutter config --no-enable-swift-package-manager` globally. | ||||||||
|
|
||||||||
| --- | ||||||||
|
|
||||||||
| ### Migrating an existing CocoaPods app to SPM | ||||||||
|
|
||||||||
| Once SPM is enabled, Flutter migrates the Xcode project automatically the next time you build or run. To migrate cleanly on an app that already uses CocoaPods: | ||||||||
|
|
||||||||
| **1. Enable SPM** (per-project block above, or global flag). | ||||||||
|
|
||||||||
| **2. Clear the existing CocoaPods state** (run from your app root): | ||||||||
|
|
||||||||
| ```bash | ||||||||
| flutter clean | ||||||||
| cd ios | ||||||||
| rm -rf Pods Podfile.lock .symlinks build | ||||||||
| rm -rf ~/Library/Developer/Xcode/DerivedData/Runner-* | ||||||||
| cd .. | ||||||||
| ``` | ||||||||
|
|
||||||||
| **3. Re-resolve and trigger the migration:** | ||||||||
|
|
||||||||
| ```bash | ||||||||
| flutter pub get | ||||||||
| flutter build ios --config-only # or: flutter run | ||||||||
| ``` | ||||||||
|
|
||||||||
| On this build Flutter adds a `FlutterGeneratedPluginSwiftPackage` to the Xcode project and resolves every plugin that ships a `Package.swift` (including `clevertap_plugin`) via SPM. Plugins that only ship a podspec stay on CocoaPods - a hybrid setup is expected. | ||||||||
|
|
||||||||
| **4. Verify SPM took over.** Open `ios/Runner.xcworkspace` and confirm: | ||||||||
| - **Package Dependencies** lists `clevertap_plugin` and, transitively, `clevertap-ios-sdk` at `7.8.1`. | ||||||||
| - The regenerated `Podfile.lock` no longer contains a `clevertap_plugin` pod entry. | ||||||||
|
|
||||||||
| **5. Build and run** on a simulator/device and smoke-test CleverTap initialization plus a `recordEvent` to confirm native symbols link. | ||||||||
|
|
||||||||
| --- | ||||||||
|
|
||||||||
| ### AppDelegate changes when using SPM | ||||||||
|
|
||||||||
| When Flutter resolves the plugin via SPM, the module name changes. Use conditional imports in your `AppDelegate` to support both CocoaPods and SPM builds: | ||||||||
|
|
||||||||
| **Objective-C** | ||||||||
|
|
||||||||
| ```objc | ||||||||
| #if __has_include(<CleverTapSDK/CleverTap.h>) | ||||||||
| #import <CleverTapSDK/CleverTap.h> | ||||||||
| #else | ||||||||
| #import "CleverTap.h" | ||||||||
| #endif | ||||||||
|
|
||||||||
| #if __has_include(<clevertap_plugin/CleverTapPlugin.h>) | ||||||||
| #import <clevertap_plugin/CleverTapPlugin.h> | ||||||||
| #else | ||||||||
| #import "CleverTapPlugin.h" | ||||||||
| #endif | ||||||||
| ``` | ||||||||
|
|
||||||||
| **Swift** | ||||||||
|
|
||||||||
| ```swift | ||||||||
| import CleverTapSDK | ||||||||
| import clevertap_plugin | ||||||||
| ``` | ||||||||
|
|
||||||||
| These guards ensure your app compiles correctly regardless of which package manager resolved the dependency. | ||||||||
|
|
||||||||
| --- | ||||||||
|
|
||||||||
| ### CocoaPods vs SPM | ||||||||
|
|
||||||||
| | | CocoaPods | SPM | | ||||||||
| |---|---|---| | ||||||||
| | SDK version pinned | `7.8.1` | `7.8.1` | | ||||||||
| | Deployment target | `9.0` | `13.0` | | ||||||||
| | Opt-in required | No | Yes on Flutter 3.24–3.43; default from 3.44 | | ||||||||
|
|
||||||||
| Both package managers resolve the same CleverTap iOS SDK version to ensure no drift between the two integration paths. | ||||||||
|
|
||||||||
| --- | ||||||||
|
|
||||||||
| ### Troubleshooting | ||||||||
|
|
||||||||
| - **Xcode doesn't resolve the package**: Run `flutter pub get`, then in Xcode go to **File → Packages → Resolve Package Versions**. | ||||||||
| - **Automatic migration didn't add the package**: In Xcode, add it manually - **Package Dependencies → Add Local…** → select `ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage` and add it to the `Runner` target. Then under **Product → Scheme → Edit Scheme → Build → Pre-actions**, add a Run Script (with build settings from `Runner`): `"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" prepare`. | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🌐 Web query:
💡 Result: As of Flutter 3.44, Swift Package Manager (SwiftPM) integration for iOS and macOS is enabled by default [1]. When you run your Flutter app, Flutter automatically manages the necessary SwiftPM dependencies, specifically the FlutterGeneratedPluginSwiftPackage, which handles dependencies for Flutter plugins compatible with SwiftPM [1][2]. Manual integration is generally only required if automatic setup fails or if you are configuring a custom Xcode target [1]. To manually integrate Swift Package Manager for a Flutter app, follow these steps [1]: 1. Add the Package Dependency: In Xcode, navigate to your project and locate the FlutterGeneratedPluginSwiftPackage at the following path: ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage [1]. Add this package to your project [1]. 2. Configure Target Dependency: Ensure that the FlutterGeneratedPluginSwiftPackage is correctly added to your desired target (e.g., the Runner target) [1]. 3. Frameworks, Libraries, and Embedded Content: In your Xcode project, go to the General tab for your target and verify that the FlutterGeneratedPluginSwiftPackage appears under the Frameworks, Libraries, and Embedded Content section [1]. If it is missing, add it manually [1]. 4. Pre-action Script: Ensure the Run Prepare Flutter Framework script is configured to run as a pre-action in your Xcode scheme [1]. Verify that the FlutterGeneratedPluginSwiftPackage is set as a target dependency in this process [1]. For "Add-to-App" scenarios where you are embedding a Flutter module into an existing native iOS or macOS application, you should instead use the command line to generate the Swift package: - Run Citations:
🌐 Web query:
💡 Result: When migrating to or configuring Swift Package Manager (SwiftPM) for Flutter applications, handling Run Script dependencies is a critical step to ensure that Flutter-specific build processes (such as assembling assets and framework generation) are correctly integrated into the Xcode build pipeline [1][2][3]. If you are manually setting up or migrating an iOS or macOS project to use SwiftPM for Flutter, you must configure two specific types of Run Script actions to maintain build integrity [2][3]. 1. Pre-action Run Script (Scheme Level) You must define a pre-action script in your Xcode Scheme to ensure the Flutter framework is prepared before the main build begins [1][2]. - Navigate to Product > Scheme > Edit Scheme... in Xcode [2][3]. - Select Build (in the left sidebar) and expand Pre-actions [2][3]. - Click the + button and select New Run Script Action [2][3]. - In the Provide build settings from dropdown, select your main Runner target [1][2]. - Use the following command (adjusting the path as necessary for your project setup): /bin/sh $FLUTTER_SWIFT_PACKAGE_OUTPUT/Scripts/flutter_integration.sh prebuild 2. Run Script Build Phase (Target Level) To handle assembly during the build process, add a Run Script Phase to your project's target [2][3]. - Navigate to your target's Build Phases tab [2][3]. - Click the + button and select New Run Script Phase [2][3]. - Set the script content to: /bin/sh Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "Target file:"
fd -a 'Integrate-iOS-SPM\.md$' . || true
file="$(fd 'Integrate-iOS-SPM\.md$' . | head -n1 || true)"
if [ -n "${file:-}" ]; then
echo
echo "Relevant lines around 115-130:"
sed -n '115,130p' "$file" | nl -ba -v115
fi
echo
echo "Search for SwiftPM instruction terms in repository:"
rg -n "Framework.?Libraries.?Embedded|Target Dependency|target dependency|FlutterGeneratedPluginSwiftPackage|Run Script|Pre-actions|xcode_backend\.sh prepare" .Repository: CleverTap/clevertap-flutter Length of output: 295 🏁 Script executed: #!/bin/sh
set -eu
file="doc/Integrate-iOS-SPM.md"
printf 'Target file: %s\n' "$file"
echo
printf 'Relevant lines around 115-130:\n'
awk 'NR>=115 && NR<=130 { printf "%4d %s\n", NR, $0 }' "$file"
echo
printf 'SwiftPM instruction terms in repository:\n'
rg -n "Framework.?Libraries.?Embedded|Target Dependency|target dependency|FlutterGeneratedPluginSwiftPackage|Run Script|Pre-actions|xcode_backend\.sh prepare" . || trueRepository: CleverTap/clevertap-flutter Length of output: 2778 Complete the manual SwiftPM linkage steps. If 🤖 Prompt for AI Agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @reshab-code check this comment
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. talked to akash regarding this, no change needed was our conclusion after discussion There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||
| - **Duplicate symbol errors**: Don't use both CocoaPods and SPM for the same plugin at once. If a `Podfile` exists and SPM is enabled, Flutter prefers SPM for plugins that support it. | ||||||||
| - **Build fails with missing headers**: Verify your deployment target is `13.0` or higher in both your Xcode project and `Podfile`/`Package.swift`. | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
printf 'Repository files matching Integrate-iOS-SPM:\n'
fd -a 'Integrate-iOS-SPM\.md$|Package\.swift$|Podfile' . || true
printf '\nRelevant doc snippet:\n'
if [ -f doc/Integrate-iOS-SPM.md ]; then
nl -ba doc/Integrate-iOS-SPM.md | sed -n '100,145p'
fi
printf '\nSearch for missing-header/deployment-target mentions:\n'
rg -n "missing header|header|deployment target|deployment-target|13\.0|FlutterGeneratedPluginSwiftPackage|conditional import|conditional imports" .Repository: CleverTap/clevertap-flutter Length of output: 454 🌐 Web query:
💡 Result: When experiencing build issues with Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
printf 'Relevant doc snippet:\n'
awk 'NR>=110 && NR<=130 {printf "%6d\t%s\n", NR, $0}' doc/Integrate-iOS-SPM.md
printf '\nPackage.swift header/public headers section:\n'
awk '{printf "%6d\t%s\n", NR, $0}' ios/clevertap_plugin/Package.swift
printf '\nPodfile platform/deployment settings and generated package mentions:\n'
awk '{printf "%6d\t%s\n", NR, $0}' example/ios/Podfile | rg -n "platform|ios|FlutterGeneratedPluginSwiftPackage|package" || true
printf '\nDoc mentions of FlutterGeneratedPluginSwiftPackage and missing headers:\n'
rg -n "FlutterGeneratedPluginSwiftPackage|missing header|header|deployment target|13\.0" doc/Integrate-iOS-SPM.md || true
printf '\nSearch for conditional import mentions in doc and plugin source headers package config:\n'
rg -n "conditional import|conditional imports|import.*conditional|Swift\(`#if`" doc ios example -S || trueRepository: CleverTap/clevertap-flutter Length of output: 4741 Separate missing-header and deployment-target troubleshooting. Deployment target errors are handled separately. Missing-header errors need SPM linkage and header-location checks, not just a 13.0 deployment target. Suggested documentation update-- **Build fails with missing headers**: Verify your deployment target is `13.0` or higher in both your Xcode project and `Podfile`/`Package.swift`.
+- **Build reports an iOS deployment-target error**: Verify that your Xcode project and `Podfile`/`Package.swift` use an iOS deployment target of `13.0` or higher.
+- **Build fails with missing headers**: Verify that `FlutterGeneratedPluginSwiftPackage` is linked to the `Runner` target and that the conditional imports match the selected package manager.📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @reshab-code check this comment
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. talked to akash regarding this, no change needed was our conclusion after discussion There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||
| - **Stale CocoaPods references after migrating**: If the build still runs `[CP]` Pods build phases (or duplicate-embeds a framework) for a plugin that has moved to SPM, run `pod deintegrate` in your app's `ios/` directory, then `flutter clean && flutter build ios` so Flutter re-integrates cleanly. Use this only as a recovery step - it is not part of the normal migration flow. Flutter regenerates the CocoaPods integration on every build, so a manual `pod deintegrate` is otherwise undone on the next build. | ||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| // swift-tools-version: 5.9 | ||
| import PackageDescription | ||
|
|
||
| let package = Package( | ||
| name: "clevertap_plugin", | ||
| platforms: [ | ||
| .iOS("13.0") | ||
| ], | ||
| products: [ | ||
| .library(name: "clevertap-plugin", targets: ["clevertap_plugin"]) | ||
| ], | ||
| dependencies: [ | ||
| .package(name: "FlutterFramework", path: "../FlutterFramework"), | ||
| .package( | ||
| url: "https://github.com/CleverTap/clevertap-ios-sdk", | ||
| exact: "7.8.1" | ||
| ) | ||
| ], | ||
| targets: [ | ||
| .target( | ||
| name: "clevertap_plugin", | ||
| dependencies: [ | ||
| .product(name: "FlutterFramework", package: "FlutterFramework"), | ||
| .product(name: "CleverTapSDK", package: "clevertap-ios-sdk") | ||
| ], | ||
| publicHeadersPath: "include/clevertap_plugin", | ||
|
reshab-code marked this conversation as resolved.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is publicHeadersPath added even if not stated in flutter docs? Is it needed? Have you tested this on sample app?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. publicHeadersPath is required for Objective-C targets in SPM as iit tells the package manager where the public headers are so it can generate a module map. The headers physically live at Sources/clevertap_plugin/include/clevertap_plugin/ and the path "include/clevertap_plugin" is relative to that sources dir. Without it, ObjC imports would break. It was tested and verified in the example app with SPM enabled. |
||
| cSettings: [ | ||
| .headerSearchPath("include/clevertap_plugin") | ||
| ] | ||
| ) | ||
| ] | ||
| ) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we have mentioned on doc it is supported from 3.24, have you tested on lower versions with this package, like 3.35 version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Testing was done on Flutter 3.41.9 with SPM opt-in enabled - which is in the 3.24-3.43 range. Flutter 3.24 and 3.35 were not tested specifically. We can either mention in the docs that it was tested on 3.41.9 and enabled by default from Flutter 3.44 or explicitly test on lower version whatever you suggest
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you test once on 3.35 or lower version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay sure