跪拜 Guibai
← All articles
Flutter · Swift

Adding Swift Package Manager Support to a Flutter Plugin Without Breaking CocoaPods

By LinXunFeng ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

CocoaPods enters read-only maintenance in December 2026, and Flutter 3.44 already defaults to SPM. A plugin without a Package.swift is invisible to any project that has switched over, which will soon be the majority. This walkthrough shows how to add SPM support as a non-breaking addition—existing CocoaPods users see zero change—while avoiding the silent fallback trap that makes you think SPM works when it doesn't.

Summary

From Flutter 3.44 onward, Swift Package Manager is the default dependency mechanism for iOS plugins, and the CocoaPods trunk goes read-only in December 2026. Plugins that lack a Package.swift simply disappear for pure-SPM projects. The fix is not to replace the podspec but to add a Package.swift alongside it, pointing both at the same source files and the same pre-compiled xcframework.

The migration moves source code into the Sources/<target>/ layout that SPM requires, updates the podspec's source_files to match, and adds a binaryTarget that fetches the identical GitHub Release zip that CocoaPods already downloads via prepare_command. A FlutterFramework dependency, injected by the Flutter tool at build time, supplies the Flutter module import—but only exists in Flutter 3.44+, which sets the hard minimum version for SPM users.

Verification is the trickiest part: Flutter's fallback logic silently routes through CocoaPods if anything is missing, so the only reliable test is checking whether the plugin appears in Podfile.lock. The final checklist covers eight concrete steps, from moving source files to aligning minimum iOS versions and ignoring SPM build artifacts.

Takeaways
Keep the podspec; Flutter's tooling picks SPM when a Package.swift exists and falls back to CocoaPods otherwise, so both files coexist without conflict.
Move source code into Sources/<target>/ and update podspec's source_files to point at the new location so both build systems compile the same files.
Use binaryTarget in Package.swift to reference the identical GitHub Release zip that CocoaPods already downloads, keeping both paths on the same binary.
The checksum on a binaryTarget is mandatory—compute it with swift package compute-checksum or SPM will reject the package.
The Flutter module import in SPM comes from a FlutterFramework dependency injected at build time, and that dependency only exists in Flutter 3.44+, which sets the minimum Flutter version for SPM users.
Align the minimum iOS version in both podspec and Package.swift; the SPM/Flutter stack requires iOS 13.0 or higher.
Verification must check Podfile.lock: if the plugin appears there, CocoaPods is active; if it's absent but the app builds, SPM is actually in control.
Turning off SPM via flutter config does not remove the integration already written into the Xcode project; a full cleanup requires deleting Package Dependencies and a pre-action script manually.
Conclusions

Flutter's silent fallback from SPM to CocoaPods is the biggest footgun in this migration—a plugin can compile fine in testing while never actually exercising the new SPM path, giving false confidence.

The FlutterFramework dependency that makes SPM work is also what locks the plugin to Flutter 3.44+, creating a hard version split: SPM users must be on the latest Flutter, while CocoaPods users face no such constraint.

Keeping the podspec and Package.swift pointed at the same binary artifact turns a potential fork into a single-source setup, but it also creates a release-process hazard—every xcframework update must touch both files or the two user populations diverge silently.

Concepts & terms
binaryTarget
A Swift Package Manager target type that references a pre-compiled binary (xcframework) hosted at a remote URL, with a mandatory checksum for integrity verification.
FlutterFramework dependency
A Swift package dependency injected by the Flutter build tool (3.44+) that provides the Flutter module import to plugin targets compiled under Swift Package Manager.
SPM fallback mechanism
Flutter's build system checks for a Package.swift in a plugin; if present and SPM is enabled, it uses SPM. Otherwise it silently falls back to the podspec, which can mask migration errors during testing.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗