Skip to content

chore(samples): xcode 27, no cocoapods, SPM re-enabled - #2951

Open
renefloor wants to merge 7 commits into
masterfrom
claude/remove-cocoapods-ios-macos-d3c5d5
Open

chore(samples): xcode 27, no cocoapods, SPM re-enabled#2951
renefloor wants to merge 7 commits into
masterfrom
claude/remove-cocoapods-ios-macos-d3c5d5

Conversation

@renefloor

@renefloor renefloor commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

🎯 Goal

Get every sample in the repo onto Xcode 27 — sample_app and the five per-package example apps. While checking, CocoaPods turned out to be removable entirely, and the SPM signing failure from #2672 turned out to be fixable rather than permanent.

🛠 Implementation details

  • Solves chore(samples): disable Swift Package Manager for sample_app #2672 / fix(ci): disable SPM at CI level instead of via pubspec flag #2675. FLUTTER_XCODE_CODE_SIGN_IDENTITY / ..._PROVISIONING_PROFILE_SPECIFIER reach xcodebuild as command-line settings, so they land on every SPM package bundle too → Signing for "X" requires a development team (40+ targets). CocoaPods was immune only because podhelper.rb sets CODE_SIGNING_ALLOWED = NO on pod resource bundles. build_ipa now writes both settings into ios/Flutter/Signing.xcconfig, which only the Runner target reads. SPM stays on; the Disable Swift Package Manager steps are gone.
  • CocoaPods removed from iOS and macOS. media_kit_video was the last pod-only plugin and was dead weight — its desktop branch is gated on Windows/Linux, which sample_app has no runner directories for, so it always fell through to DefaultStreamVideoPlayer. Dropped it, deintegrated pods, deleted both Podfiles and the cocoapods gem.
  • Crashlytics upload-symbols now globbed from the SPM checkout instead of ios/Pods/.
  • macOS deployment target 11.5 → 12.0 — Flutter 3.47's FlutterMacOS.podspec floor, below which pod install refused to resolve.
  • The example apps too. They pinned iOS 11/12 and macOS 10.11–11.0, all below Flutter 3.47's floors, so none of them built at all; four Podfiles also declared a RunnerTests target no project defines. Targets raised, stanza dropped, CocoaPods deintegrated there as well — every plugin they use is already a Swift Package. Flutter's pending project migrations (@main, FlutterImplicitEngineDelegate, UIApplicationSceneManifest) came along with the builds.
  • CI: iOS jobs on the xcode-27 image behind a new shared .github/actions/setup-xcode (one place for the version); select_xcode dropped from the Fastfile since it can't express "latest" and was overriding the workflows with a pinned 26.2 the image lacks; e2e jobs pin 26.2 explicitly; pod cache action deleted.

☑️ Verification

distribute_internal is green on this branch — run 34455350823. Both iOS jobs signed, symbolicated and shipped (Firebase App Distribution + Successfully uploaded package to App Store Connect) with zero signing errors and zero pod install. Locally sample_app also builds and runs on an iOS 27 simulator and macOS 26.6.2, and all seven example-app configurations build from a wiped build/ with no Podfile regenerated and no pod install. Note CI never builds the examples — no workflow references them and melos run analyze skips them — so those seven builds are their only coverage.

☑️Contributor Checklist

General

  • Assigned a person / code owner group (required)
  • Thread with the PR link started in a respective Slack channel (#flutter-team) (required)
  • PR is linked to the GitHub issue it resolves

☑️Reviewer Checklist

  • Sample runs & works
  • UI Changes correct (before & after images)
  • Bugs validated (bugfixes)
  • New feature tested and works
  • All code we touched has new or updated Documentation

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Build & Platform Updates

    • Example iOS projects now require iOS 15 or later; macOS examples require macOS 12 or later.
    • Example applications now use Swift Package Manager for Flutter plugin integration.
    • Updated Flutter app lifecycle and scene configuration for current Apple platform tooling.
  • Video Playback

    • The sample app now uses the SDK’s default video player instead of custom desktop playback support.
  • Release Engineering

    • Updated Xcode setup and dependency caching for more consistent iOS and macOS builds.

Moves the sample app's iOS build to Swift Package Manager and removes
the CocoaPods integration from both the iOS and macOS Runner projects.

`media_kit_video` was the only remaining pod-only plugin. It backed
`SampleAppVideoPlayer`, whose desktop branch is gated on Windows/Linux —
platforms the sample app has no runner directories for — so on every
platform it can actually be built for the widget already fell through to
`DefaultStreamVideoPlayer`. Dropping the dependency leaves iOS, macOS,
Android and web behaviour unchanged and makes every remaining plugin
SPM-compatible.

macOS deployment target goes 11.5 -> 12.0, the floor Flutter 3.47
declares in its generated `FlutterMacOS.podspec` and uses in its own
app template; below it the build fails to resolve. The highest floor
any plugin asks for is 11.0.

CI iOS jobs move to the `xcode-27` runner image with `xcode-version:
latest`, and the "Disable Swift Package Manager" steps are gone — with
them the tool would regenerate a Podfile and link plugins through
CocoaPods again. The pod cache action is deleted along with its last
consumers; the SwiftPM equivalent is ~1.5 GB, too large to be worth
caching.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 465585f3-4930-47e3-a252-d91577fc5604

📥 Commits

Reviewing files that changed from the base of the PR and between e253712 and fecd043.

📒 Files selected for processing (3)
  • .github/workflows/e2e_test.yml
  • .github/workflows/e2e_test_cron.yml
  • sample_app/ios/fastlane/Fastfile
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/e2e_test.yml
  • .github/workflows/e2e_test_cron.yml

Included review availability: Your plan provides up to 4 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

The change removes CocoaPods from Flutter Apple examples, links Flutter-generated Swift packages, raises Apple deployment targets, updates Flutter app lifecycle configuration, removes the sample desktop media player, and centralizes Xcode setup in reusable CI actions.

Changes

Apple platform migration

Layer / File(s) Summary
CI and sample app migration
.github/..., sample_app/...
CI now uses reusable Xcode setup and SwiftPM caching. The sample app removes CocoaPods and media-kit integration, adds Swift package linkage, and moves signing settings to Signing.xcconfig.
stream_chat iOS example
packages/stream_chat/example/ios/...
The example updates iOS deployment settings, scene configuration, Flutter implicit engine plugin registration, and Swift package project wiring.
stream_chat_flutter Apple examples
packages/stream_chat_flutter/example/{ios,macos}/...
The examples remove CocoaPods, use FlutterGeneratedPluginSwiftPackage, update deployment targets and schemes, and migrate app lifecycle configuration.
stream_chat_localizations iOS example
packages/stream_chat_localizations/example/ios/...
The example adopts Swift Package Manager, raises the iOS deployment target, adds scene configuration, and moves plugin registration to the implicit engine callback.
stream_chat_persistence Apple examples
packages/stream_chat_persistence/example/{ios,macos}/...
The examples remove CocoaPods, link the generated Swift package, update platform targets and schemes, and update app delegate behavior.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Workflow
  participant SetupXcode
  participant FlutterBuild
  participant SwiftPM
  Workflow->>SetupXcode: Select Xcode version
  Workflow->>FlutterBuild: Run Flutter bootstrap
  Workflow->>SwiftPM: Restore Swift package cache
  FlutterBuild->>SwiftPM: Build with FlutterGeneratedPluginSwiftPackage
Loading

Suggested reviewers: xsahil03x, testableapple

Merge Risk: ⚪ Minimal · up to fecd0

The change migrates Apple examples to Swift Package Manager, removes CocoaPods, and updates CI and platform settings. Reported builds and local runs pass, with no concrete merge-blocking risk identified.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 14 functions across 7 files. (3 skipped: 3… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: Xcode 27 support, CocoaPods removal, and Swift Package Manager re-enablement across sample applications.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 14 functions across 7 files. (3 skipped: 3 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/remove-cocoapods-ios-macos-d3c5d5

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.85%. Comparing base (0e3b364) to head (fecd043).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #2951   +/-   ##
=======================================
  Coverage   75.85%   75.85%           
=======================================
  Files         442      442           
  Lines       28776    28776           
=======================================
  Hits        21828    21828           
  Misses       6948     6948           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@renefloor
renefloor marked this pull request as draft September 10, 2026 08:11
renefloor and others added 2 commits September 10, 2026 10:17
Restores what #2672 / #2675 worked around, rather than re-disabling SPM.

`FLUTTER_XCODE_CODE_SIGN_IDENTITY` / `..._PROVISIONING_PROFILE_SPECIFIER`
reach xcodebuild as command-line build settings, and those apply to every
target in the build graph. With CocoaPods that was harmless — podhelper.rb
sets `CODE_SIGNING_ALLOWED = NO` on pod resource bundles and on
Pods-Runner. Swift Package plugin bundles have no such hook, so they
inherited the app's manual identity and failed the archive with
`Signing for "<plugin>" requires a development team` — 40+ of them.

Writing the same two settings into Flutter/Signing.xcconfig instead fixes
it: Flutter/Release.xcconfig is the base configuration of the Runner
target alone (the project-level configurations have no xcconfig), and
Runner's Release config leaves identity and profile unset, so an xcconfig
can supply them. Verified locally via `fastlane ios build_ipa
export_method:"app-store"`: zero package signing errors, only the
expected missing distribution certificate. `no_codesign:true` still
archives successfully and removes any stale generated file.

The env-var approach itself is still needed in spirit because
flutter/flutter#113977 is open; only its scope changes.

Xcode selection moves to a shared .github/actions/setup-xcode, defaulting
to the newest Xcode on the image. The Fastfile no longer calls
`select_xcode` — it cannot express "latest", since it resolves to
/Applications/Xcode_<version>.app, and it was silently overriding the
workflows' own choice with a pinned 26.2 that the xcode-27 image does not
have. The two e2e jobs opt into 26.2 explicitly: they run on macos-15,
whose default is Xcode 16.4, and their pinned simulator runtimes need 26.x.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`upload_dsyms_to_crashlytics` pointed `binary_path` at
ios/Pods/FirebaseCrashlytics/upload-symbols, which no longer exists now
that the sample app has no CocoaPods integration. Both iOS distribute
jobs archived, signed and exported their IPAs successfully and then died
here:

    [!] Couldn't find file at path
        '.../sample_app/ios/Pods/FirebaseCrashlytics/upload-symbols'

Under Swift Package Manager the binary lives in the firebase-ios-sdk
checkout instead. fastlane's own discovery only searches Pods/ and
Fabric.app, so the path is globbed and passed explicitly, with a clear
error if it moves again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@renefloor renefloor changed the title chore(samples): xcode 27, no more cocoapods chore(samples): xcode 27, no cocoapods, SPM re-enabled Sep 10, 2026
renefloor and others added 2 commits September 10, 2026 10:46
The comment claimed macos-15 defaults to Xcode 16.4 and therefore needs a
26.x toolchain. The image default is indeed 16.4, but setup-xcode's
'latest' resolves to the newest Xcode installed, which on macos-15 is
26.3 — so that was never the reason.

The actual constraint is runtime alignment: macos-15 ships iOS simulator
runtimes 18.5, 18.6, 26.0, 26.1 and 26.2, but no 26.3.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The per-package examples pinned deployment targets far below what Flutter
3.47 supports, so none of them could build. Raises iOS to 15.0 and macOS
to 12.0 — the values Flutter's own app template uses, and for macOS the
floor its generated FlutterMacOS.podspec declares, below which `pod
install` refuses to resolve.

Four Podfiles also declared a `RunnerTests` target that no project
defines, which failed `pod install` outright:

    [!] Unable to find a target named `RunnerTests` in project
        `Runner.xcodeproj`, did find `Runner`.

stream_chat_flutter's two Podfiles already had that stanza commented out,
which is why only those examples still built.

Every plugin these examples use is a Swift Package, so Flutter resolved
them all through SPM and then asked for CocoaPods to be removed by hand:

    All plugins found for ios are Swift Packages, but your project still
    has CocoaPods integration. [...] will need to be migrated to Swift
    Package Manager manually.

Done here, so the six Podfiles are gone rather than fixed: `pod
deintegrate` on each project, Podfiles deleted, `Pods-Runner` includes
stripped from all twelve xcconfigs, and `Pods/Pods.xcodeproj` dropped
from the workspaces. Some of those includes were a hard `#include`, so
removing the Pods directory alone would have broken the build outright.

Building each example also applied Flutter 3.47's pending project
migrations — `@main`, `FlutterImplicitEngineDelegate`,
`UIApplicationSceneManifest`, and dropping the stale `MinimumOSVersion`
from AppFrameworkInfo.plist.

All seven configurations verified from a wiped build directory on Xcode
27.0 / Flutter 3.47.0 — no Podfile regenerated, no `pod install`, and no
migration warning: stream_chat, stream_chat_flutter,
stream_chat_flutter_core, stream_chat_localizations and
stream_chat_persistence on iOS, plus stream_chat_flutter and
stream_chat_persistence on macOS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@renefloor
renefloor marked this pull request as ready for review September 10, 2026 08:56
Comment thread .github/actions/cache-swiftpm/action.yml Outdated
Comment thread .gitignore Outdated
@renefloor
renefloor force-pushed the claude/remove-cocoapods-ios-macos-d3c5d5 branch from e253712 to 2f3b7b2 Compare September 10, 2026 10:58

@xsahil03x xsahil03x left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only looked at the workflows and the Fastfile — the pbxproj/Podfile churn I'm taking on trust from the seven local builds.

The signing fix is the right shape, and I convinced myself it actually holds: the Runner target's Release config declares neither CODE_SIGN_IDENTITY nor PROVISIONING_PROFILE_SPECIFIER (and this PR doesn't touch that), so the xcconfig values flow through; Profile shares Release.xcconfig as its base but overrides both at target level, so a profile build can't accidentally pick up the match profile. Generated.xcconfig has no signing keys either, so the include order is fine. Nice that select_xcode going away also takes the allure-lane exclusion list with it.

Thanks for dispatching the iOS e2e — that was the one path the green distribute_internal run didn't cover. Batch 0 is clean: no pod install, no Podfile regenerated, Xcode is set to 26.2.0 (17C52), 13 + 27 tests passed.

Dropping the pod cache also costs nothing — build (ios) came in at 6m45s here against 10–12m on recent runs.

Two comment fixes and one suggestion inline.

Unrelated to the code: build (ios) runs on every PR, so this puts larger-runner minutes on a public repo on the default path — you'd know better than me whether that's within budget.

Comment thread .github/workflows/e2e_test.yml Outdated
Comment thread .github/workflows/e2e_test_cron.yml Outdated
Comment thread sample_app/ios/fastlane/Fastfile Outdated
Comment on lines 179 to 185
UI.user_error!('Could not find Crashlytics upload-symbols in build/ios/SourcePackages') if upload_symbols.nil?

upload_symbols_to_crashlytics(
dsym_path: dsym_zip,
gsp_path: "#{root_path}/ios/Runner/GoogleService-Info.plist",
binary_path: "#{root_path}/ios/Pods/FirebaseCrashlytics/upload-symbols"
binary_path: upload_symbols
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

build/ios/SourcePackages is the Flutter tool's -clonedSourcePackagesDirPath, and the * absorbs whatever firebase-ios-sdk names its checkout — both can drift under a Flutter or Firebase bump. Because this lane runs before firebase_app_distribution / upload_to_testflight, a drift there hard-fails an already-built release over a symbol upload. dSYMs can be re-uploaded after the fact; a failed release costs a full rerun.

Suggested change
UI.user_error!('Could not find Crashlytics upload-symbols in build/ios/SourcePackages') if upload_symbols.nil?
upload_symbols_to_crashlytics(
dsym_path: dsym_zip,
gsp_path: "#{root_path}/ios/Runner/GoogleService-Info.plist",
binary_path: "#{root_path}/ios/Pods/FirebaseCrashlytics/upload-symbols"
binary_path: upload_symbols
)
if upload_symbols.nil?
UI.important('Could not find Crashlytics upload-symbols under build/ios/SourcePackages; skipping dSYM upload')
next
end
upload_symbols_to_crashlytics(
dsym_path: dsym_zip,
gsp_path: "#{root_path}/ios/Runner/GoogleService-Info.plist",
binary_path: upload_symbols
)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, and thanks — this was a real bug, not a style call. Confirmed the ordering you describe in all three release lanes: build_ipaupload_dsyms_to_crashlyticsfirebase_app_distribution / upload_to_testflight, so a glob miss aborted an already-built, already-signed release. The asymmetry you point at is the deciding argument: dSYMs can be uploaded after the fact, a failed release costs a full rebuild.

Applied in fecd043, with one deviation: UI.error instead of UI.important. Both are non-fatal, but the failure mode of skipping is silent — Crashlytics quietly goes unsymbolicated and nobody notices for months. Red in the log makes that harder to miss. Happy to switch to UI.important if you'd rather keep it a warning.

Also took your framing for the comment, since "outside our control" is the durable reason the branch has to exist at all:

# Both the SourcePackages directory and the checkout name are outside our
# control, so a miss is possible. This lane runs before the app is
# distributed: symbols can be uploaded later, a failed release cannot.

renefloor and others added 2 commits September 10, 2026 14:51
Keeps the mechanism a future reader needs to avoid reintroducing the bug,
drops the history and the point-in-time cache measurements — those belong
in the pull request.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
upload_dsyms_to_crashlytics runs after build_ipa but before
firebase_app_distribution and upload_to_testflight, so a missed glob
aborted an already-built, signed release over a symbol upload. Both the
SourcePackages directory and the firebase-ios-sdk checkout name can move
under a Flutter or Firebase bump.

Symbols can be uploaded after the fact; a failed release costs a full
rebuild. Logs an error and skips instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants