Skip to content

feat(shorebird_cli): generate podspec for iOS framework releases#3675

Open
eseidel wants to merge 4 commits into
mainfrom
eseidel/ios-framework-cocoapods
Open

feat(shorebird_cli): generate podspec for iOS framework releases#3675
eseidel wants to merge 4 commits into
mainfrom
eseidel/ios-framework-cocoapods

Conversation

@eseidel
Copy link
Copy Markdown
Contributor

@eseidel eseidel commented Apr 2, 2026

Summary

  • Generates a ShorebirdFlutter.podspec in the release/ directory alongside the existing xcframeworks during shorebird release ios-framework
  • Updates post-release instructions to show both CocoaPods and manual Xcode embedding options

This enables users to integrate Shorebird's iOS framework add-to-app via CocoaPods (pod 'ShorebirdFlutter', :path => 'release') instead of manually configuring Xcode framework search paths and embedding.

Context: iOS add-to-app integration approaches

Flutter documents two main iOS add-to-app integration methods:

  • Option A: CocoaPods — lower friction, manages framework linking automatically
  • Option B: Manual framework embedding — what Shorebird supports today

We have customer demand for CocoaPods-based integration. There are two possible approaches:

Approach A (this PR): Wrap existing xcframeworks in a podspec

The build process is unchanged — we still produce App.xcframework and ShorebirdFlutter.xcframework. We just additionally generate a podspec that wraps them so users can do pod install instead of manual Xcode configuration.

Pros:

  • Minimal change — just generates a podspec file alongside existing output
  • No changes to the build pipeline, patching infrastructure, or artifact uploads
  • No project modifications required (user still runs shorebird release ios-framework as before)
  • The CocoaPods integration step is no different from what they'd do for any framework dependency

Cons:

  • Not the "full" CocoaPods add-to-app experience — Flutter code doesn't recompile automatically during native Xcode builds
  • User still runs shorebird release ios-framework separately when Flutter code changes
  • Doesn't match Flutter's documented "Option A" workflow exactly (which compiles Flutter from source during pod install)

Approach B (not in this PR): Hook into source-compilation CocoaPods flow

This would be the "real" Option A support where Flutter compiles from source as part of pod install. This would require:

  • Injecting Shorebird's engine into the CocoaPods build pipeline
  • Users modifying their project (Podfile, env vars, or build scripts) to use Shorebird's Flutter fork
  • Understanding and hooking into Flutter's iOS build system at the podspec level

This breaks Shorebird's current "no project modifications" advantage and is significantly more complex. Worth exploring only if Approach A doesn't satisfy customer demand.

Test plan

  • Existing ios_framework_releaser_test.dart tests pass
  • New test verifies podspec is generated with correct content
  • Post-release instructions test updated for new two-option format
  • Manual: run shorebird release ios-framework and verify release/ShorebirdFlutter.podspec is generated
  • Manual: verify pod install works in a native iOS app with the generated podspec

Generate a CocoaPods podspec alongside the existing xcframeworks in the
release directory, enabling users to integrate via `pod install` instead
of manually configuring Xcode framework search paths and embedding.
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Comment thread packages/shorebird_cli/lib/src/commands/release/ios_framework_releaser.dart Outdated
@bdero bdero self-requested a review May 4, 2026 17:22
@bdero bdero closed this May 4, 2026
@bdero bdero reopened this May 4, 2026
Copy link
Copy Markdown
Member

@bdero bdero left a comment

Choose a reason for hiding this comment

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

bdero's review bot:

Overall

Pragmatic shape — wrapping existing xcframeworks in a podspec is the right "Approach A" given customer demand and zero changes to the build pipeline. Three things in the podspec template are hardcoded that probably shouldn't be (s.version, s.platform, s.license), and the test only spot-checks two lines so template-level breakage wouldn't be caught. None of this is blocking.

One reminder unrelated to a specific line: both manual checkboxes in the test plan (shorebird release ios-framework generates the podspec; pod install works in a real iOS app) are unchecked. The unit tests prove we write a podspec; only the manual run proves CocoaPods accepts it. Want this verified before merge — it's the entire customer-visible value of the PR.

File(podspecPath).writeAsStringSync('''
Pod::Spec.new do |s|
s.name = 'ShorebirdFlutter'
s.version = '0.0.1'
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.

bdero's review bot:

💡 s.version = '0.0.1' is the same string for every release, so Podfile.lock and pod outdated will show stale info regardless of which Shorebird release the user is actually shipping. With :path => integration the version doesn't drive resolution, so nothing breaks — but plumbing through argResults['release-version'] (the same value getReleaseVersion returns) makes the lockfile actually reflect reality. One catch: Flutter's release-version can include +build suffixes, and CocoaPods' SemVer parser is stricter than pub's, so we may need to strip or transform.

s.license = { :type => 'BSD-3-Clause' }
s.author = 'Shorebird'
s.source = { :path => '.' }
s.platform = :ios, '12.0'
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.

bdero's review bot:

💡 s.platform = :ios, '12.0' is hardcoded, but the actual minimum is whatever MinimumOSVersion ends up baked into App.xcframework and ShorebirdFlutter.xcframework — driven by Flutter's minimum, which has been creeping up. CocoaPods doesn't cross-check podspec platform against framework Info.plist, so a future Flutter min-version bump would silently let users link Shorebird into iOS 12 apps where the binaries actually require 13+. Worth either reading from the xcframework's Info.plist or pulling from a Shorebird-side known-minimum constant.

s.version = '0.0.1'
s.summary = 'Shorebird Flutter framework for add-to-app integration.'
s.homepage = 'https://shorebird.dev'
s.license = { :type => 'BSD-3-Clause' }
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.

bdero's review bot:

💡 s.license = { :type => 'BSD-3-Clause' } — not sure this is accurate for the bundled binaries. The Flutter engine portion is BSD-3-Clause, but Shorebird's modifications and proprietary patching code ship in ShorebirdFlutter.xcframework too, and I think those are under different terms. Worth a check with someone who has more licensing context — at minimum, { :file => 'LICENSE' } pointing to a real bundled file is more honest than asserting one license type for the whole thing.

Comment on lines +482 to +498
test('generates podspec in release directory', () async {
await runWithOverrides(iosFrameworkReleaser.buildReleaseArtifacts);

final podspecFile = File(
p.join(projectRoot.path, 'release', 'ShorebirdFlutter.podspec'),
);
expect(podspecFile.existsSync(), isTrue);
final content = podspecFile.readAsStringSync();
expect(content, contains("s.name = 'ShorebirdFlutter'"));
expect(
content,
contains(
"s.vendored_frameworks = 'App.xcframework', "
"'ShorebirdFlutter.xcframework'",
),
);
});
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.

bdero's review bot:

💡 Test only asserts two contains checks. A heredoc typo (missing end, mismatched quotes) wouldn't be caught — pod install would discover it at runtime in production. A golden-file comparison against the full podspec content would lock down the template; it costs almost nothing and means future template edits show up clearly in diffs. Optional: shelling out to pod ipc spec would catch syntax errors but that adds a CI dependency we probably don't want.

Comment on lines +183 to +191
${styleBold.wrap('Option A: CocoaPods')}
Add the following to your app's Podfile:
${lightCyan.wrap("pod 'ShorebirdFlutter', :path => '$relativeFrameworkDirectoryPath'")}
Then run ${lightCyan.wrap('pod install')}.

Instructions for these steps can be found at https://docs.flutter.dev/add-to-app/ios/project-setup#option-b---embed-frameworks-in-xcode.
${styleBold.wrap('Option B: Manual Xcode embedding')}
1. Add the relative path to the ${lightCyan.wrap(relativeFrameworkDirectoryPath)} directory to your app's Framework Search Paths in your Xcode build settings.
2. Embed the App.xcframework and ShorebirdFlutter.xcframework in your Xcode project.
Instructions: https://docs.flutter.dev/add-to-app/ios/project-setup#option-b---embed-frameworks-in-xcode
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.

bdero's review bot:

🧹 If CocoaPods is the new recommended path, worth labeling it that way ("Option A: CocoaPods (recommended)") so users don't read Option A, glance at Option B, and feel like they need to pick. Otherwise both options read as equally weighted choices and we get more "which one should I use?" support questions.

Pod::Spec.new do |s|
s.name = 'ShorebirdFlutter'
s.version = '0.0.1'
s.summary = 'Shorebird Flutter framework for add-to-app integration.'
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.

bdero's review bot:

🧹 s.summary could mention that the framework is a Shorebird-patched Flutter engine, not stock Flutter — that's the whole point. "Shorebird's patched Flutter engine for add-to-app iOS integration" or similar. Minor.

Copy link
Copy Markdown
Member

@bdero bdero left a comment

Choose a reason for hiding this comment

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

This lgtm. Couple of things my review bot suggested that seem to make sense. Plz land at your discretion.

@bdero
Copy link
Copy Markdown
Member

bdero commented May 4, 2026

Also, IIRC, there isn't an issue we should close when this one lands, right?

@bdero
Copy link
Copy Markdown
Member

bdero commented May 11, 2026

@eseidel Looks like this is ready to go pending some clanker reviews.

@eseidel
Copy link
Copy Markdown
Contributor Author

eseidel commented May 11, 2026

It's ready to go, I'm just not sure if it solves a real customer problem or not.

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