To use CocoaPods with Capacitor 8, create the iOS platform with bunx cap add ios --packagemanager CocoaPods, install CocoaPods (Homebrew or a Gemfile with Bundler), and let bunx cap sync ios manage the Podfile and run pod install. Capacitor 8 still fully supports CocoaPods. Only the default for new projects changed to Swift Package Manager.
CocoaPods is no longer the long-term direction, and its trunk goes read-only on December 2, 2026. So the real question is when it’s still the right choice, how to keep it working, and how to leave it cleanly. This guide covers all three.
When CocoaPods is still the right choice
Stay on, or pick, CocoaPods when one of these is true:
- A plugin you need has no
Package.swift. In an SPM project the CLI warns “Some installed Capacitor plugins are not compatible with SPM” and leaves that plugin out, so it fails at runtime with “plugin is not implemented”. - A vendor SDK is distributed only as a pod. Some analytics, payment and identity SDKs still ship CocoaPods-only, or ship an xcframework you’d have to wrap yourself.
- A Cordova plugin relies on pods. Capacitor generates a Swift package for Cordova plugins in SPM projects, but complex ones with
<podspec>dependencies tend to work better with CocoaPods. - You rely on podspec subspecs to pick SDK variants. SPM has package traits now, and Capacitor supports them experimentally since CLI 8.3 (see SPM package traits in Capacitor), but the plugin has to define traits first.
- A large, customized Xcode project where a migration is not worth the risk this release cycle.
Package identity collisions used to be another reason. Since CLI 8.4 you can work around them in SPM with experimental.ios.spm.packageOptions (symlink or moduleAliases) in your Capacitor config.
If none of these apply, use SPM. It needs no Ruby toolchain, no Podfile and no .xcworkspace.
CocoaPods vs SPM in Capacitor 8
| CocoaPods | Swift Package Manager | |
|---|---|---|
Default for new cap add ios |
No | Yes |
| Extra tooling | Ruby + CocoaPods gem | None, built into Xcode |
| Project to open | ios/App/App.xcworkspace |
ios/App/App.xcodeproj |
| Plugin requirement | .podspec |
Package.swift |
| Generated file | def capacitor_pods block in Podfile |
ios/App/CapApp-SPM/Package.swift |
| Optional features | Subspecs | Package traits (experimental in Capacitor) |
| Future | Trunk read-only from Dec 2, 2026 | Apple-supported |
Install CocoaPods
Pick one approach and use it everywhere, including CI.
Homebrew (simplest for one developer):
brew install cocoapods
pod --version
Bundler (pins the version for the whole team, recommended for CI). Create a Gemfile in the project root or in ios/App:
source 'https://rubygems.org'
gem 'cocoapods', '~> 1.16'
bundle install
When the Capacitor CLI finds a Gemfile that mentions CocoaPods, it runs bundle exec pod install instead of pod install. If pod lives somewhere unusual, set CAPACITOR_COCOAPODS_PATH to the binary.
Create a Capacitor 8 app with CocoaPods
bun add @capacitor/ios
bun run build
bunx cap add ios --packagemanager CocoaPods
bunx cap open ios
cap open ios opens App.xcworkspace. Always build from the workspace. Opening App.xcodeproj directly gives you No such module 'Capacitor'.
The Podfile, explained
This is the Podfile Capacitor 8 generates:
require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers'
platform :ios, '15.0'
use_frameworks!
install! 'cocoapods', :disable_input_output_paths => true
def capacitor_pods
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
end
target 'App' do
capacitor_pods
# Add your Pods here
end
post_install do |installer|
assertDeploymentTarget(installer)
end
What you can and can’t touch:
def capacitor_pods ... endbelongs to the CLI. On every sync it replaces the whole block with onepodline per plugin, pointing to the plugin’s folder innode_modules. Anything you add inside is lost.- Add your own pods under
target 'App', aftercapacitor_pods. assertDeploymentTargetraises any pod with a deployment target below 15.0 up to 15.0, which avoids Xcode warnings about unsupported targets. Keep it, and add your ownpost_installlogic inside the same block.platform :iosmust be at least 15.0 for Capacitor 8.
Example with an extra pod and a build setting fix:
target 'App' do
capacitor_pods
pod 'GoogleMLKit/BarcodeScanning', '~> 7.0'
end
post_install do |installer|
assertDeploymentTarget(installer)
installer.pods_project.targets.each do |target|
if target.respond_to?(:product_type) && target.product_type == 'com.apple.product-type.bundle'
target.build_configurations.each do |config|
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
end
end
end
end
The loop disables code signing for resource bundle targets. It fixes Signing for "X" requires a development team on pods that ship resource bundles. Only add it if you hit that error.
Day-to-day commands
# After installing or removing a plugin
bunx cap sync ios
# Use the exact versions in Podfile.lock (good for CI)
bunx cap sync ios --deployment
# Refresh the spec repo when a new pod version isn't found
cd ios/App && pod repo update && pod install
# Update one pod past its locked version
cd ios/App && pod update FirebaseMessaging
cap sync ios also runs xcodebuild clean on the project after pod install, so you don’t need a manual clean after adding plugins.
Commit Podfile and Podfile.lock. Don’t commit ios/App/Pods/. It’s regenerated by pod install.
Switching an existing app from SPM to CocoaPods
There’s no reverse migration command. Do it by hand:
- Commit, then copy
ios/somewhere safe. - Delete
ios/. - Run
bunx cap add ios --packagemanager CocoaPods. - Reapply native changes:
Info.plistkeys, entitlements and capabilities, signing team, bundle identifier, app icons and splash assets, URL schemes, extensions (widgets, notification service), and any custom Swift inAppDelegate. bunx cap sync iosand build.
Diffing the old and new ios/App/App folders is the fastest way to catch what you missed.
Fixing common CocoaPods errors
CocoaPods could not find compatible versions for pod "X". Either your local spec repo is stale or two pods require incompatible versions. Run pod install --repo-update in ios/App. If it still fails, read the dependency chain in the error, find the plugin with the strict pin, upgrade it or patch its podspec.
The sandbox is not in sync with the Podfile.lock. Run 'pod install'. The Pods folder doesn’t match the lock file, usually after switching branches. Run bunx cap sync ios.
[!] Unable to find a specification for 'X'. The spec repo cache is stale or the pod name is wrong. Run pod repo update.
Unable to find compatibility version string for object version '70' (or 77). Your Xcode project uses a newer format than your CocoaPods/xcodeproj gem understands. Update CocoaPods to the latest release. If you can’t, back up project.pbxproj and set objectVersion to a value your CocoaPods supports.
Sandbox: rsync(...) deny(1) file-write-create. Xcode’s user script sandboxing blocks the CocoaPods embed script. In Build Settings, set User Script Sandboxing (ENABLE_USER_SCRIPT_SANDBOXING) to No for the App target.
No such module 'Capacitor'. You opened App.xcodeproj. Open App.xcworkspace, or run bunx cap open ios.
pod: command not found in CI. Install CocoaPods in the job, or use Bundler with a cached vendor/bundle. Without it, the CLI logs “Skipping pod install because CocoaPods is not installed” and the build fails later.
More iOS build errors are covered in the Capacitor iOS troubleshooting guide.
The CocoaPods trunk read-only date
The CocoaPods maintainers announced that trunk, the central spec repository, becomes read-only on December 2, 2026, after a test run in early November 2026. What that means for a Capacitor app:
- Existing builds keep working. Every published podspec stays available.
- No new versions through trunk. SDKs you pull from trunk (Firebase, Google ML Kit, vendor SDKs) stop getting updates there. Security fixes and new iOS SDK support will only ship through SPM or other channels.
- Capacitor plugins themselves are not affected directly. They’re installed from npm and referenced by
:pathin the Podfile. Their transitive pod dependencies are.
Treat CocoaPods as a bridge. List which dependency keeps you on it, and revisit when that dependency adds SPM support.
Moving off CocoaPods
When you’re ready:
bunx cap spm-migration-assistant
It runs pod deintegrate, removes the Podfile, Podfile.lock and App.xcworkspace, creates CapApp-SPM, and writes a debug.xcconfig. You then add CapApp-SPM as a local package in Xcode and set debug.xcconfig as the Debug configuration file. The full walkthrough is in how to migrate your Capacitor app to SPM, and the trade-offs are covered in SPM vs CocoaPods for Capacitor.
Plugin authors who need to add SPM support should read migrate a Capacitor plugin to SPM.
If managing Ruby, CocoaPods and Xcode versions on build machines is the painful part, Capgo Build builds CocoaPods and SPM projects in the cloud on current Xcode.