Migrating from Cocoapods to Swift Package Manager

DX
@developer_x October 26, 2023, 10:15 AM

Hey everyone,

I'm looking to migrate a large existing iOS project from using Cocoapods to the native Swift Package Manager (SPM). While SPM offers great benefits, the migration process for a project with many dependencies can be tricky. I've encountered a few hurdles and was hoping to get some community insights and best practices.

Specifically, I'm struggling with:

  • Identifying equivalent SPM packages for all our current Cocoapods.
  • Handling private dependencies (e.g., internal libraries).
  • Ensuring compatibility and resolving version conflicts.
  • Strategies for a phased or incremental migration.

Has anyone successfully navigated this transition? What tools or scripts did you find helpful? Any common pitfalls to watch out for?

Here's a snippet of our current Podfile for context:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '15.0'
target 'MyApp' do
use_frameworks!
pod 'Alamofire', '5.6.4'
pod 'Kingfisher', '7.0.0'
pod 'Firebase/Core', '9.0.0'
pod 'RealmSwift', '10.24.0'
// Internal library pod
pod 'MyInternalLibrary', :git => 'https://github.com/mycompany/MyInternalLibrary.git', :tag => '1.2.0'
end

Looking forward to hearing your experiences!

Like (15) Reply Quote
SK
@swift_kid October 26, 2023, 10:45 AM

Hi @developer_x, great topic! We recently completed a similar migration for our main app, which had around 50 dependencies. It was a significant undertaking, but definitely worth it.

For finding SPM equivalents, the Cocoapods.org website is good, but also checking the GitHub repos of popular libraries is key. Many now have a `.swift-version` file or a `Package.swift` in their root.

We found this tool incredibly useful for mapping dependencies: Tuist. It can help manage your Xcode projects and can convert Podfiles to SPM. Even if you don't use Tuist for your main project management, its conversion logic can be instructive.

For private repos, SPM natively supports them. You'll need to configure your SSH keys or use personal access tokens. You can define them directly in your `Package.swift` like this:

let package = Package(
name: "MyApp",
platforms: [.iOS(.v15)],
dependencies: [
.package(url: "https://github.com/Alamofire/Alamofire.git", .exact("5.6.4")),
.package(url: "https://github.com/onevcat/Kingfisher.git", .exact("7.0.0")),
.package(url: "https://github.com/firebase/firebase-ios-sdk.git", .branch("main")), // Note: Firebase SPM support is evolving, check their docs for specific versions
.package(url: "https://github.com/realm/realm-swift.git", .exact("10.24.0")),
// Private repository example
.package(url: "git@github.com:mycompany/MyInternalLibrary.git", .exact("1.2.0"))
],
targets: [
.target(name: "MyApp", dependencies: [
.product(name: "Alamofire", package: "Alamofire"),
.product(name: "Kingfisher", package: "Kingfisher"),
// Adjust Firebase target name based on SDK documentation
"RealmSwift",
"MyInternalLibrary"
])
]
)

The key for private repos is to use the SSH URL if your build environment supports it. Otherwise, you might need to explore token-based authentication for HTTPS.

For version conflicts, SPM's resolution strategy is generally quite robust. However, if you have complex interdependencies, it might be worth isolating problematic dependencies in a separate test project to debug. Always start with specific versions (`.exact` or `.upToNextMajor`) and only use `.branch` or `.revision` for development/testing.

We did a phased migration: first, we replaced one or two less critical dependencies, built, tested, and committed. Then we tackled larger chunks. It reduces the risk of breaking the entire project.

Good luck!

Like (28) Reply Quote
MA
@mobile_architect October 26, 2023, 11:05 AM

Echoing @swift_kid's points. The Firebase SPM setup can be a bit dynamic. Last I checked, you often need to include several sub-packages like `.package(url: "https://github.com/firebase/firebase-ios-sdk.git", .branch("main"))` and then specify targets like `FirebaseCore`, `FirebaseAnalytics` etc., in your main target's dependencies.

Also, for a smoother transition, consider using a build system tool like Bazel or Tuist. They abstract away much of the complexity of managing dependencies across different systems (Cocoapods, SPM, Carthage) and can help manage the transition more gracefully by defining your project structure in a unified way.

One common pitfall: Make sure your `Package.swift` file is correctly placed in the root of your Xcode project or within a workspace. Also, ensure your Xcode version is reasonably up-to-date, as SPM support has been continuously improved.

Like (18) Reply Quote

Leave a Reply