CLAUDE LABJP
FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as OctoberFORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Articles/Claude Code
Claude Code/2026-06-01Intermediate

Moving Firebase From CocoaPods to SPM With Claude Code as a Partner: An Implementation Memo

A record of migrating Firebase from CocoaPods to Swift Package Manager in an indie iOS app. What I handed to Claude Code, what I decided myself, and the Crashlytics dSYM trap I hit along the way.

Claude Code197Firebase4Swift Package Manager2iOS24Crashlytics8

The longer an app stays in production, the heavier its "old assumptions" about the build environment quietly become. For me, that weight was Firebase. I have been building iOS apps on my own since 2014, and the cumulative downloads have passed 50 million. Many of those apps rely on Firebase, and CocoaPods—a natural choice back when I adopted it—had slowly turned into a source of friction every time I dealt with build times on Apple Silicon or privacy manifest requirements.

This time I moved just one app, out of several, over to Swift Package Manager (SPM). The migration itself was straightforward; what proved genuinely useful was drawing the line between what I handed to Claude Code and what I decided myself. Here is that record.

Why move to SPM now

To be honest, CocoaPods still worked. Touching something that works is always a little scary. I went ahead anyway for three reasons.

First, clean build times. Any flow that includes pod install quietly eats time, both on CI and locally. Second, Firebase's SPM distribution has become reliable enough since Xcode 15. I used to hear plenty of horror stories about the SPM version, but today it installs cleanly. Third, privacy manifests. The Firebase SDK now ships its manifest, and I find the path through SPM clearer to reason about.

The flip side is that apps not suffering from any of these don't need to move at all. "Respect what works, and fix things in the order the pain appears" is, in my experience, how you keep an indie practice going for the long haul.

What I actually did

The migration broke down into three stages: remove CocoaPods, reinstall via SPM, and fix the build phases.

First, strip Firebase out of the Podfile.

# Before (Podfile)
target 'MyWallpaperApp' do
  use_frameworks!
  pod 'Firebase/Analytics'
  pod 'Firebase/Crashlytics'
  pod 'Firebase/Messaging'
  pod 'SomeOtherLib'   # keep non-Firebase pods as they are
end
 
# After (Podfile) — delete the three Firebase lines
target 'MyWallpaperApp' do
  use_frameworks!
  pod 'SomeOtherLib'
end

If you depend on pods other than Firebase, it is safer to leave the Podfile intact and remove only the Firebase lines. Then tidy up the CocoaPods footprint.

# Reflect the removed Firebase lines
pod install
 
# Only if no non-Firebase pods remain, remove CocoaPods entirely
# pod deintegrate
# rm Podfile Podfile.lock
# rm -rf Pods
# From here on, open the .xcodeproj, not the .xcworkspace

Next, add Firebase via SPM in Xcode. From File → Add Package Dependencies, point at firebase-ios-sdk and attach only the products you need to your target (in my case FirebaseAnalytics, FirebaseCrashlytics, and FirebaseMessaging). Resisting the urge to add everything here pays off later in build time. Your existing GoogleService-Info.plist works as-is, so there is nothing to swap.

Up to this point, what I handed to Claude Code was the before/after diff of the Podfile and the changed sections of project.pbxproj. Having it mechanically surface leftover references from the CocoaPods era—like Base Configuration pointing at Pods-*.xcconfig—was the single most helpful use, the kind of thing human eyes tend to skip.

The Crashlytics dSYM that tripped me up

Just when the migration looked smooth, the Crashlytics dSYM upload caught my foot at the very end. The Run Script from the CocoaPods era relied on a ${PODS_ROOT} path like this.

# The breaking case (a leftover from CocoaPods)
"${PODS_ROOT}/FirebaseCrashlytics/run"

SPM has no Pods directory, so this path naturally fails to resolve: the build still succeeds, but symbolicated crash reports stop arriving. It is an easy failure to miss.

The correct approach is to add a new Run Script to Build Phases that calls the run script inside the package SPM checked out, and to declare the dSYM and Info.plist paths explicitly in Input Files.

# Run Script after the SPM migration (add to Build Phases)
"${BUILD_DIR%/Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run"
# Add to the same Run Script's Input Files
${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}
$(SRCROOT)/$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)

To arrive at this path, I pasted the build log straight into Claude Code. It separated the two symptoms—"the dSYM can't be found" and "the run script is in the wrong place"—which spared me a round of blind path-hunting. Handing over the entire error message and asking it to form a hypothesis about the cause is, I find, an especially good fit for environment-dependent trouble like this.

What I delegated, and what I decided

The clearest lesson this time was the division of labor with Claude Code.

Mechanical work—surfacing leftover references, isolating causes from a build log, drafting a Run Script template—was faster and more accurate to delegate. The judgment calls, on the other hand, stayed firmly mine to the end: which app to move first, how much to change at once, and whether to migrate now or defer.

Both of my grandfathers were temple carpenters. The sense that working with your hands is itself a kind of devotion sits at my core. However many convenient tools appear, the responsibility for deciding where to make the cut is something I take on myself—and as long as I hold that line, a migration with an AI partner becomes deeply reassuring. The speed of the work and the weight of the decision are healthier kept apart.

What comes next

Now that one app has established the pattern, I will roll the same steps out to the rest in turn. My next two verification themes are how far the privacy manifest is pulled in automatically through SPM, and how to fold version pinning via Package.resolved into my release process.

If you have been feeling that something "still works, but I'd like to move it soon," I hope this record helps. Thank you for reading to the end.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Claude Code2026-06-04
Clearing Crashlytics 'Missing dSYM' Warnings with Claude Code: A Field Memo
Right after moving Firebase to SPM, my Crashlytics reports stopped symbolicating and showed raw addresses. Here is how I narrowed down the Missing dSYM cause with Claude Code and rebuilt an upload path that does not break again.
Claude Code2026-05-24
Automating iOS Crashlytics Triage with Claude Code — A Production Pipeline from dSYM Symbolication to Draft PR
How I rebuilt iOS crash triage at our 50M+ download app studio: Firebase Crashlytics issues flow into a Cloud Functions + Claude Code pipeline that handles dSYM symbolication, blast-radius estimation, and a draft fix PR in under 90 seconds. Real numbers, real code, real lessons.
Claude Code2026-05-17
Six Months Until CocoaPods Shutdown — Migrating Beautiful HD Wallpapers to Firebase SPM with Claude Code
Firebase Apple SDK is dropping CocoaPods in October 2026. Here's how I migrated Beautiful HD Wallpapers — one of four iOS apps I run as an indie developer — to Swift Package Manager, and where Claude Code made the difference.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →