effidevFlutter · Cloudflare edge · Cloud cost optimization
English

Flutter Swift Package Manager: CocoaPods Migration Guide

Flutter Swift Package Manager CocoaPods migration architecture diagram

The End of CocoaPods Has Begun

On December 2, 2026, the CocoaPods trunk registry will transition to permanent read-only mode. This means you will no longer be able to publish new pods or update existing ones. It marks the effective end of the dependency management tool that Flutter iOS developers have relied on for nearly a decade.

The Flutter team has been preparing Swift Package Manager (SwiftPM) support since 2024. In Flutter 3.44, SwiftPM became the default dependency manager for iOS/macOS. Running flutter run automatically prompts the Flutter CLI to update your Xcode project to use SwiftPM.

This guide covers both perspectives:

  1. App Developers: How to migrate an existing CocoaPods-based Flutter project to SwiftPM
  2. Plugin Authors: How to add Package.swift so your pub.dev plugin supports SwiftPM

If you don’t migrate now, your iOS dependencies will effectively be frozen after December 2026.

CocoaPods vs Swift Package Manager: Key Differences

Feature CocoaPods Swift Package Manager
Maintainer Independent open-source project Official Apple support
Config File Podfile Package.swift
Xcode Integration Generates .xcworkspace Native Xcode integration
Build Speed Slow (Ruby-based preprocessing) Fast (Native)
Binary Caching Limited Built-in (.build cache)
Post-2026 Status Read-only (End of Life) Actively developed
pub.dev Score Penalty applied Preferred

The biggest advantage of SwiftPM is its native integration with Xcode. You no longer need a separate pod install, and you can build directly using .xcodeproj instead of a .xcworkspace file.

App Developers: Automatic Migration

If you are using Flutter 3.44 or higher, migration happens automatically in most cases.

1. Check Your Flutter Version

flutter --version
# Check if Flutter is 3.44.0 or higher

flutter upgrade  # Upgrade if necessary

2. Trigger Automatic Migration

# Run in your existing project to let the Flutter CLI automatically attempt SwiftPM migration
flutter run

# Or explicitly enable it
flutter config --enable-swift-package-manager
flutter run  # Subsequent runs will automatically apply SwiftPM settings

What the Flutter CLI automatically performs during migration:

  1. Adds SwiftPM integration configuration to ios/Runner.xcodeproj/project.pbxproj
  2. Adds a reference to FlutterGeneratedPluginSwiftPackage in the ios/ directory
  3. Adds Run Prepare Flutter Framework Script to the Xcode build scheme

3. Verify Successful Migration

# Check in Xcode
open ios/Runner.xcworkspace

# Or in the terminal
cat ios/Runner.xcodeproj/project.pbxproj | grep "FlutterGeneratedPluginSwiftPackage"
# If this string is present, SwiftPM integration is complete

In Xcode under the Project Navigator → Package Dependencies tab, plugins will be displayed as SwiftPM packages.

4. Handling Unsupported Plugins

If any of your plugins do not support SwiftPM yet, the Flutter CLI displays a warning:

The following packages have native iOS code but are not compatible with Swift Package Manager:
  - some_plugin (version: 1.2.3)

Flutter will use CocoaPods for these plugins only.

In this scenario, Flutter automatically falls back: supported plugins use SwiftPM, while unsupported plugins continue to use CocoaPods. Your Podfile remains intact and is applied only to plugins that still require pod install.

How to handle unsupported plugins:

  1. Open a SwiftPM support issue on the plugin’s GitHub repository
  2. Search for alternative plugins (check for SwiftPM support badges on pub.dev)
  3. Contribute directly by submitting a PR with Package.swift

5. Temporarily Disabling (If Issues Arise)

If SwiftPM causes build errors, you can temporarily disable it:

# pubspec.yaml — Temporarily disable
flutter:
  config:
    enable-swift-package-manager: false

Or:

flutter config --no-enable-swift-package-manager

⚠️ This option is a temporary workaround. It is expected to be removed around 2027, so do not rely on it long term.

App Developers: Manual Migration (Legacy Projects)

If automatic migration fails or if you are migrating an older project, follow these manual steps.

Pre-Migration Checklist

# Backup current state
git checkout -b feat/spm-migration
git add -A && git commit -m "chore: before SPM migration"

# Check SwiftPM support for all plugins
cat pubspec.yaml | grep -A 50 "dependencies:"

Check for the “Swift Package Manager support” badge on each Flutter plugin’s pub.dev page.

Cleaning Up Podfile

# ios/Podfile (Retained for plugin fallbacks post-migration)
platform :ios, '13.0'  # Verify minimum iOS version

# Supported plugins are automatically excluded after SwiftPM migration
# Explicitly specify CocoaPods-only plugins (if needed)
target 'Runner' do
  use_frameworks!
  use_modular_headers!

  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))

  target 'RunnerTests' do
    inherit! :search_paths
  end
end

Executing Migration

cd ios

# Clean existing CocoaPods cache
rm -rf Pods/ Podfile.lock

# Flutter Clean
cd ..
flutter clean

# Reinstall dependencies (Hybrid SwiftPM + CocoaPods)
flutter pub get
cd ios && pod install  # Only for plugins that still require it
cd ..

# Build test
flutter build ios --no-codesign

Plugin Authors: Adding Package.swift

If you publish a Flutter plugin on pub.dev, you must add SwiftPM support. The pub.dev scoring system now includes SwiftPM support as an evaluation metric, meaning unsupported plugins will receive a score penalty.

Directory Structure Changes

Previous CocoaPods Structure:

ios/
  Classes/
    MyPlugin.swift
    MyPlugin.h (if mixing Obj-C)
  my_plugin.podspec

Structure After Adding SwiftPM Support:

ios/
  my_plugin/           # SwiftPM package root (newly added)
    Package.swift      # SwiftPM manifest
    Sources/
      my_plugin/       # Target name = Package name
        MyPlugin.swift
  Classes/             # Retained for CocoaPods backward compatibility
    MyPlugin.swift
  my_plugin.podspec    # Retained existing podspec

SwiftPM strictly requires source files to be located inside the package root. While CocoaPods allowed paths outside the root, SwiftPM strictly enforces package-internal paths.

Writing Package.swift

For pure Swift plugins:

// ios/my_plugin/Package.swift
// swift-tools-version: 5.9
import PackageDescription

let package = Package(
    name: "my_plugin",
    platforms: [
        .iOS(.v13),
    ],
    products: [
        .library(
            name: "my-plugin",
            targets: ["my_plugin"]
        )
    ],
    dependencies: [],
    targets: [
        .target(
            name: "my_plugin",
            dependencies: [],
            path: "Sources/my_plugin",
            // If you have external Swift dependencies:
            // dependencies: [
            //   .product(name: "SomeSDK", package: "some-sdk"),
            // ]
        )
    ]
)

For mixed Swift + Objective-C plugins:

// ios/my_plugin/Package.swift
// swift-tools-version: 5.9
import PackageDescription

let package = Package(
    name: "my_plugin",
    platforms: [
        .iOS(.v13),
    ],
    products: [
        .library(name: "my-plugin", targets: ["my_plugin"])
    ],
    targets: [
        .target(
            name: "my_plugin",
            dependencies: [],
            path: "Sources/my_plugin",
            // Specify public header path for Obj-C
            publicHeadersPath: "include",
            // Obj-C files are included in the same target
            cSettings: [
                .headerSearchPath("include"),
            ]
        )
    ]
)

SwiftPM cannot mix Swift and Objective-C within the same target. If your plugin uses both Objective-C and Swift, you must separate them into distinct targets:

targets: [
    // Objective-C target only
    .target(
        name: "my_plugin_objc",
        path: "Sources/my_plugin_objc",
        publicHeadersPath: "include"
    ),
    // Swift target depends on Obj-C target
    .target(
        name: "my_plugin",
        dependencies: ["my_plugin_objc"],
        path: "Sources/my_plugin"
    )
]

Rearranging Source File Structure

# Create source directory for SwiftPM
mkdir -p ios/my_plugin/Sources/my_plugin

# Copy existing sources (retain originals for CocoaPods)
cp ios/Classes/MyPlugin.swift ios/my_plugin/Sources/my_plugin/
cp ios/Classes/MyPlugin.m ios/my_plugin/Sources/my_plugin/  # If Obj-C is used

Adding Flutter Framework Dependency

If you use Flutter native APIs (such as the FlutterPlugin protocol or FlutterMethodChannel), you must declare the Flutter Framework as a dependency:

// ios/my_plugin/Package.swift
let package = Package(
    name: "my_plugin",
    platforms: [.iOS(.v13)],
    products: [
        .library(name: "my-plugin", targets: ["my_plugin"])
    ],
    dependencies: [
        // Add Flutter Framework dependency (Required!)
        .package(
            url: "https://github.com/nicehash/flutter",
            from: "1.0.0"
        )
    ],
    targets: [
        .target(
            name: "my_plugin",
            dependencies: [
                .product(name: "Flutter", package: "flutter")
            ],
            path: "Sources/my_plugin"
        )
    ]
)

Note: For the official Flutter Package.swift URL, use the path provided by the Flutter team. In practice, the Flutter CLI automatically injects the bundled framework path from the Flutter SDK when executed.

Updating pubspec.yaml

# pubspec.yaml (Plugin)
name: my_plugin
version: 2.0.0

flutter:
  plugin:
    platforms:
      ios:
        # CocoaPods configuration (backward compatibility)
        podspec: ios/my_plugin.podspec
        # SwiftPM configuration added
        swiftPackage: ios/my_plugin

Verification: Testing with an Example App

# Enable SwiftPM migration in the plugin's example app
cd example

flutter config --enable-swift-package-manager
flutter run -d iPhone  # Physical device or simulator

# Check in Xcode
open ios/Runner.xcworkspace
# If my_plugin appears under Package Dependencies, migration succeeded

Updating the CI Pipeline

# .github/workflows/test.yml
- name: Flutter iOS Build (SwiftPM)
  run: |
    flutter config --enable-swift-package-manager
    flutter build ios --no-codesign --simulator
  env:
    FLUTTER_VERSION: "3.44.0"

# Parallel test for CocoaPods build (verify backward compatibility)
- name: Flutter iOS Build (CocoaPods fallback)
  run: |
    flutter config --no-enable-swift-package-manager
    flutter build ios --no-codesign --simulator

Migration Timeline and Risk Matrix

Timeline Event Risk Level
Flutter 3.44 (Current) SwiftPM becomes default Low (Fallback available)
December 2, 2026 CocoaPods trunk read-only High
Early 2027 (Expected) Opt-out option removed from Flutter Very High

Action items for today:

Must complete before December 2026:

Troubleshooting: Common Errors

Error 1: “Sources folder not found”

error: Source files for target 'my_plugin' should be located under 'Sources/my_plugin'

Cause: SwiftPM requires source files to be located under Sources/<target_name>/ within the package root.
Solution: Move source files to ios/my_plugin/Sources/my_plugin/.

Error 2: “Cannot use Swift and Objective-C in the same target”

Cause: SwiftPM does not support mixing languages within a single target.
Solution: Separate into an Objective-C target and a Swift target.

Error 3: Still Using CocoaPods After pod install

# Check forced SwiftPM mode
flutter config | grep swift-package-manager

# If not enabled
flutter config --enable-swift-package-manager
flutter clean && flutter pub get

Error 4: “FlutterGeneratedPluginSwiftPackage not found”

# Clear Xcode cache
rm -rf ~/Library/Developer/Xcode/DerivedData
cd ios && xcodebuild -resolvePackageDependencies

Error 5: Bitcode Warnings

SwiftPM disables Bitcode by default. Since Apple deprecated Bitcode starting with iOS 16, you can safely ignore this warning.

Impact on pub.dev Scores

Starting in 2025, pub.dev included SwiftPM support as part of its plugin score calculations. Score categories:

Support Level pub.dev Score Impact
SwiftPM + CocoaPods both supported Full points
CocoaPods only supported Penalty (~10 points deducted)
Unsupported (Excludes Dart-only plugins) Additional warning displayed

Your pub.dev score out of 100 directly influences search ranking. SwiftPM support is no longer optional—it’s mandatory.

Conclusion

The sunset of CocoaPods marks one of the most significant shifts in the Flutter iOS ecosystem. Transitioning to Swift Package Manager is not just a tool swap; it’s a return to Apple’s official ecosystem.

The good news for app developers is that in most cases, a single flutter run handles the automatic migration. Issues generally arise only when you depend on plugins that do not yet support SwiftPM.

If you are a plugin author, you should add Package.swift right away. Publishing new versions to CocoaPods will become impossible after December 2026.

Action plan:

  1. Run flutter config --enable-swift-package-manager
  2. Test your build with flutter run
  3. Review warned plugins and establish remediation plans
  4. (Plugin authors) Add Package.swift and publish to pub.dev

Related post: Check out Android-side optimization for Flutter apps in Flutter 16KB Page Size and Google Play Optimization.