Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@
p.join(targetLibraryDirectory.path, 'ShorebirdFlutter.xcframework'),
);

// Generate a podspec so users can integrate via CocoaPods if preferred.
_writePodspec(targetLibraryDirectory);

return targetLibraryDirectory;
}

Expand Down Expand Up @@ -148,18 +151,44 @@
),
);

/// Writes a podspec file that wraps the release xcframeworks, enabling

Check warning on line 154 in packages/shorebird_cli/lib/src/commands/release/ios_framework_releaser.dart

View workflow job for this annotation

GitHub Actions / 🔤 Check Spelling / build

Unknown word (xcframeworks)
/// CocoaPods-based integration as an alternative to manual Xcode embedding.
void _writePodspec(Directory releaseDir) {
final podspecPath = p.join(
releaseDir.path,
'ShorebirdFlutter.podspec',
);
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.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.

s.homepage = 'https://shorebird.dev'
s.license = { :type => 'Proprietary' }
Comment thread
eseidel marked this conversation as resolved.
Outdated
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.vendored_frameworks = 'App.xcframework', 'ShorebirdFlutter.xcframework'
end
''');
}

@override
String get postReleaseInstructions {
final relativeFrameworkDirectoryPath = p.relative(releaseDirectory.path);
return '''

Your next step is to add the .xcframework files found in the ${lightCyan.wrap(relativeFrameworkDirectoryPath)} directory to your iOS app.

To do this:
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.framework in your Xcode project.
${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
Comment on lines +183 to +191
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.

''';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,24 @@ void main() {
verify(() => artifactBuilder.buildIosFramework(args: [])).called(1);
});

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'",
),
);
});
Comment on lines +482 to +498
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.


group('when --obfuscate is passed', () {
setUp(() {
when(() => argResults['obfuscate']).thenReturn(true);
Expand Down Expand Up @@ -686,18 +704,22 @@ void main() {
final relativeFrameworkDirectoryPath = p.relative(
p.join(projectRoot.path, 'release'),
);
final instructions = runWithOverrides(
() => iosFrameworkReleaser.postReleaseInstructions,
);
expect(instructions, contains('Option A: CocoaPods'));
expect(instructions, contains('Option B: Manual Xcode embedding'));
expect(
runWithOverrides(() => iosFrameworkReleaser.postReleaseInstructions),
equals('''

Your next step is to add the .xcframework files found in the ${lightCyan.wrap(relativeFrameworkDirectoryPath)} directory to your iOS app.

To do this:
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.framework in your Xcode project.

Instructions for these steps can be found at https://docs.flutter.dev/add-to-app/ios/project-setup#option-b---embed-frameworks-in-xcode.
'''),
instructions,
contains(
"pod 'ShorebirdFlutter', :path => "
"'$relativeFrameworkDirectoryPath'",
),
);
expect(instructions, contains('pod install'));
expect(
instructions,
contains('App.xcframework and ShorebirdFlutter.xcframework'),
);
});
});
Expand Down
Loading