Flutter Says Goodbye to CocoaPods: The Inevitable Migration to Swift Package Manager

Flutter Says Goodbye to CocoaPods: The Inevitable Migration to Swift Package Manager

Why Flutter is abandoning CocoaPods by December 2026, what it means for your project, and how to migrate without breaking anything.

By Omar Flores

Imagine you run a water supply company for a city. For decades, your system has worked. Pipes are everywhere. Developers of applications know how to connect to it. There are millions of lines of documentation about how to use your infrastructure.

But five years ago, you started noticing the problems. Security vulnerabilities appear more and more often. Your team is aging. There isn’t enough money to maintain it. So you make a decision: you announce that within two years, the old system will stop receiving new connections. Within three, it will shut down completely.

That is exactly what is happening with CocoaPods in the Flutter ecosystem.

CocoaPods won’t disappear tomorrow. But Flutter has already decided: Swift Package Manager is the future. On December 1, 2026, CocoaPods trunk will become read-only. There will be no new pods, no new versions, no security updates. And while your old builds will still work, if you need a new version of a dependency or want to add a new plugin, you won’t be able to. You’ll be stuck.

For most Flutter app developers, this is barely background noise. The CLI handles the migration automatically. You run flutter run and done, your project updates. But for plugin maintainers, the story is completely different. Today, 39% of the top 100 iOS plugins still haven’t migrated. That means when CocoaPods closes, there will be a momentary chaos in the ecosystem.

This article is not a step-by-step migration guide. That already exists in the official documentation. This is an analysis of why this is happening, what it really means, and how to prepare for a change that seems small but is actually profound.


The Problem Nobody Mentions: CocoaPods Is in Maintenance Mode

CocoaPods was revolutionary a decade ago. It provided centralized dependency management for iOS in a way Apple never offered. It worked. Millions of apps were built with it. Developers who didn’t have to fight with linkers and search paths loved it.

But here’s the uncomfortable truth: CocoaPods was built to fill a gap that Apple decided to fill with its own mechanism. In 2015, Apple launched Swift. Then, in 2021, Swift Package Manager integrated completely into Xcode. It’s no longer a third party. It’s Apple’s official tool for managing dependencies in the Apple ecosystem.

That means CocoaPods is now an unnecessary intermediary.

I’ve seen this happen before in other ecosystems. When a tool stops being the canonical choice, an inevitably slow process begins. Maintainers don’t disappear overnight. They keep fixing things. They keep responding to issues. But there’s no energy for big changes anymore. There’s no vision for the future. It’s just maintenance.

And then, at some point, the maintainers decide: “No, this is unsustainable.” The CocoaPods team wrote a post months ago: the CocoaPods registry would become read-only. December 1, 2026. There’s no negotiation. It’s a hard deadline.

Why does this matter? Because it means that in eight months, nobody will be able to publish new pod versions to the CocoaPods trunk. If today you publish version 2.0 of your plugin because you found a critical security bug, in eight months you won’t be able to publish version 2.1. The system will be frozen.

For app developers, this is mainly an inconvenience. For plugin maintainers, it’s existential.


Swift Package Manager Is More Than Just a Technical Change

Swift Package Manager arrived as an alternative, but it’s more than that. It’s the direction Apple decided to take. And when Apple decides a direction, the rest of the iOS ecosystem eventually follows.

Swift Package Manager has real advantages. It’s not just marketing.

First, it simplifies Flutter installation. If you’re a new Flutter developer, today you need to install Ruby, then CocoaPods, then wait while everything downloads. It’s a significant friction point. With Swift Package Manager, everything comes bundled with Xcode. If you have Xcode, you have Swift Package Manager.

Second, it opens access to the entire Swift package ecosystem. If you’re a plugin maintainer and need a dependency, today you’re limited to pods that exist in CocoaPods. With Swift Package Manager, you can use any package in the Swift ecosystem. That means more options, better code, less duplication of functionality.

Third, security. It’s not a Swift Package Manager feature itself, but it’s connected. CocoaPods allows pods to run arbitrary scripts during compilation. It’s one of the biggest attack vectors in the iOS ecosystem. The CocoaPods team has spent the last few months blocking the prepare_command capability in new pods because security researchers were constantly abusing it. Swift Package Manager has a more restrictive model. Packages can do less, but that means there’s less attack surface.

All that said: this is not an easy change. It’s a significant change. It touches the foundation of how Flutter plugins are built on iOS.


What Changes for App Developers: The Good News

If you’re someone who simply builds a Flutter app, here’s what you need to know: the migration is probably completely automatic.

When you run flutter run with Swift Package Manager enabled, the CLI will detect that your project isn’t migrated. Then it will automatically update your project to use Swift Package Manager. It modifies two files:

  • ios/Runner.xcodeproj/project.pbxproj
  • ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme

Then, it creates a new target: FlutterGeneratedPluginSwiftPackage. This is the mechanism Flutter uses to expose all your plugins as a single Swift package that your app can depend on.

The CLI also adds a pre-action to your Xcode scheme called “Run Prepare Flutter Framework Script”. This script runs before each build, ensuring dependencies are prepared.

For most apps, that’s it. It sounds complicated, but it works.

The only scenario where this breaks is if you have plugins that haven’t migrated to Swift Package Manager. Then Flutter falls back to CocoaPods for those specific ones. It works, but it’s a temporary state. As December 1, 2026 approaches, you’ll need your plugins to update or you’ll need to change them.

There’s an emergency exit: you can disable Swift Package Manager in your pubspec.yaml:

flutter:
  config:
    enable-swift-package-manager: false

But this is a temporary solution. It’s not a long-term strategy.


The Real Complexity: Plugin Maintainers

The change is easy for apps. The change is hard for plugins.

As a plugin maintainer on iOS, you need to do several things:

  1. Create a Package.swift — Define the structure of your Swift package, the dependencies it needs, the platforms it supports.
  2. Reorganize your code — Instead of having files in ios/Classes/, you need to move them to ios/plugin_name/Sources/plugin_name/.
  3. Update resource references — If your plugin includes files, privacy manifests, or assets, they need to be in the right place within the Swift Package structure.
  4. Update your pubspec.yaml — Now requires Flutter 3.41 or higher.
  5. Add FlutterFramework as a dependency — If you migrated your plugin in 2025, this is the new required step. Flutter added a new dependency that all plugins need.

For a simple plugin, this is a weekend of work. For a complex plugin with multiple CocoaPods dependencies, multiple resources, and years of accumulated code, it can be weeks.

And here’s where the ecosystem has a real problem.

According to Flutter data, 61% of the top 100 iOS plugins have migrated. The remaining 39% are stuck. Some are abandoned. Some are waiting for their maintainers to have time. Some are waiting for their dependencies to migrate first.

This creates a cascading effect. If your plugin depends on another plugin that hasn’t migrated to Swift Package Manager, you can’t complete your migration either. You have to wait. And while you wait, the clock ticks toward December 1, 2026.

Worse yet: when CocoaPods becomes read-only, some of these plugins will have effectively disappeared from the ecosystem. Their old versions will still exist, but there will be no security updates, bug fixes, or support for new dependency versions.

For app developers who rely on stalled plugins, this means being stuck on an old plugin version, unable to access new features or security fixes.


How to Migrate: For Both Sides

For app developers, the process is simple:

  1. Update your Flutter SDK: flutter upgrade
  2. Enable Swift Package Manager: flutter config --enable-swift-package-manager
  3. Run your app: flutter run
  4. The CLI does the rest

If something breaks, you can disable it globally: flutter config --no-enable-swift-package-manager. But try to fix the issues first. Open an issue with details about which plugins are causing the problem.

For plugin maintainers, the process is more involved. The official Flutter documentation is clear and detailed. Here are the main steps:

First, create the directory structure:

my_plugin/
  ios/
    my_plugin/
      Package.swift
      Sources/
        my_plugin/

Then, your Package.swift should look like this:

// swift-tools-version: 5.9
import PackageDescription

let package = Package(
    name: "my_plugin",
    platforms: [
        .iOS("13.0"),
        .macOS("10.15")
    ],
    products: [
        .library(name: "my-plugin", targets: ["my_plugin"])
    ],
    dependencies: [
        .package(name: "FlutterFramework", path: "../FlutterFramework")
    ],
    targets: [
        .target(
            name: "my_plugin",
            dependencies: [
                .product(name: "FlutterFramework", package: "FlutterFramework")
            ],
            resources: [
                // If you have a PrivacyInfo.xcprivacy
                .process("PrivacyInfo.xcprivacy"),
            ]
        )
    ]
)

Note the new step: you need to add FlutterFramework as a dependency. If you migrated in 2025, you need to go back and add this.

Then, move all your code from ios/Classes/ to ios/my_plugin/Sources/my_plugin/. Update your imports anywhere there are relative path references.

If you have a PrivacyInfo.xcprivacy file (and with Apple’s new privacy rules, you probably do), move it to ios/my_plugin/Sources/my_plugin/PrivacyInfo.xcprivacy.

Update your pubspec.yaml:

environment:
  sdk: ^3.11.0
  flutter: ">=3.41.0"

If you use Pigeon to generate Dart-Swift code, update your Pigeon configuration:

swiftOut: 'ios/my_plugin/Sources/my_plugin/messages.g.swift',

Finally, test that your plugin still works with CocoaPods (for apps that haven’t migrated):

flutter config --no-enable-swift-package-manager
cd example/
flutter run

And that it works with Swift Package Manager:

flutter config --enable-swift-package-manager
flutter run

Common Mistakes: How Teams Break This

I’ve seen the following happen several times:

Mistake 1: Migrate the plugin but forget FlutterFramework

If you migrated in 2025, your Package.swift probably doesn’t have FlutterFramework. Flutter added this requirement in 3.41. Your plugin will compile, but fail at runtime. Symptoms: “undefined symbol” errors during linking.

The fix is simple: go to your Package.swift and add:

dependencies: [
    .package(name: "FlutterFramework", path: "../FlutterFramework")
],
targets: [
    .target(
        name: "my_plugin",
        dependencies: [
            .product(name: "FlutterFramework", package: "FlutterFramework")
        ],
    )
]

Mistake 2: Abandon CocoaPods too early

If you’re a plugin maintainer and migrate to Swift Package Manager but don’t update your documentation, your users will probably try to use your new version with CocoaPods. It will fail in mysterious ways.

Support both. Your my_plugin.podspec should point to the new location of your files:

s.source_files = 'my_plugin/Sources/my_plugin/**/*.swift'

Users on CocoaPods can still build. And users on Swift Package Manager can too.

Mistake 3: Resources in the wrong place

If your plugin includes images, privacy manifests, or any other assets, they need to be within the Swift Package structure, not outside. If you put them in the wrong place, Xcode won’t find them.

Resources must be within Sources/my_plugin/. Then, reference them in your code using Bundle.module:

#if SWIFT_PACKAGE
    let imagePath = Bundle.module.path(forResource: "myImage", ofType: "jpg")
#else
    let imagePath = Bundle(for: Self.self).path(forResource: "myImage", ofType: "jpg")
#endif

The Real Timeline: What Happens After

Here’s the official timeline:

  • Today (May 2026): Swift Package Manager is enabled by default in Flutter 3.44+. Developers can opt out, but the future is clear.
  • September-October 2026: The CocoaPods team will send mass reminders. More publicity. More pressure on plugin maintainers.
  • November 1-7, 2026: CocoaPods does a “test run” of going read-only. It’s a one-week window where the system will be read-only so automation can fail early.
  • December 1, 2026: CocoaPods trunk is permanently read-only. That’s the end.

After December 1:

  • Existing plugins in CocoaPods won’t disappear. Their old versions will still be downloadable.
  • But there will be no new versions. If you find a critical security bug, you can’t release a patch. If you need to update a dependency to support a new iOS version, you can’t.
  • App developers with abandoned plugins will be stuck. They can’t update their plugins. Eventually, their apps will age and stop working on new iOS versions.

December 1 isn’t the end of the world. But it’s the end of an era. And it’s better to prepare now than to panic on December 2.


Migration is not optional. It’s only a matter of when you accept that the future arrived, not when you deny it.