feat(flutter,ios): vendor chanora_bridge as a CocoaPods framework
iOS rejects loose .dylib loads (`dlopen` of any path outside the
app bundle is sandboxed), so a Flutter-Rust bridge has to ship
inside the .app as an Embed-and-Sign framework that
flutter_rust_bridge's runtime loader can dlopen via its default
`chanora_bridge.framework/chanora_bridge` lookup path.
This commit wires the bridge into the iOS build via a CocoaPods
podspec. Same role Cargokit plays for other Flutter+Rust setups,
done by hand against this repo's layout to avoid the Cargokit
vendoring footprint that was previously dropped.
apps/chanora_flutter/ios/chanora_bridge.podspec
New file. `prepare_command` invokes
`cargo build --release --target aarch64-apple-ios -p chanora_bridge`
with IPHONEOS_DEPLOYMENT_TARGET=13.0 +
CMAKE_POLICY_VERSION_MINIMUM=3.5 (satisfies audiopus_sys's
cmake invocation on modern CMake 4.x), then wraps the
produced libchanora_bridge.dylib into
chanora_bridge.framework with an Info.plist that declares
iPhoneOS / MinimumOSVersion=13.0, and rewrites LC_ID_DYLIB
to @rpath/chanora_bridge.framework/chanora_bridge.
`vendored_frameworks` exposes the result to CocoaPods, which
integrates it into Runner.xcodeproj with Embed & Sign
automatically. No Xcode UI edits required.
apps/chanora_flutter/ios/Podfile
Add `pod 'chanora_bridge', :path => '.'` to the Runner
target.
apps/chanora_flutter/ios/Podfile.lock
Generated by `pod install` after the pod was added. Pins
chanora_bridge 1.0.0 + checksum so iOS builds on other
developer machines pull the same framework version.
.gitignore
Add `/apps/chanora_flutter/ios/Frameworks/` so the
~13 MB built framework (regenerated on every pod install) is
not committed.
Verified end-to-end on the M1 Mac (coder@100.118.130.73):
pod install ok
chanora_bridge.framework generated at
apps/chanora_flutter/ios/Frameworks/chanora_bridge.framework
Install name: @rpath/chanora_bridge.framework/chanora_bridge
flutter build ios --release --no-codesign 12.7 s
-> Runner.app 29.9 MB (was 16.9 MB without the bridge)
-> Runner.app/Frameworks/chanora_bridge.framework present
alongside Flutter.framework, App.framework,
connectivity_plus.framework, objective_c.framework.
DEC-025: iOS / iPad officially in scope for P0.
Notes for owners installing on physical iOS devices:
The Personal Apple Team in Xcode requires a unique
PRODUCT_BUNDLE_IDENTIFIER (the default 'app.chanora.chanoraFlutter'
may already be claimed in the App Store registry). Set this
locally in Xcode -> Runner target -> Signing & Capabilities ->
Bundle Identifier (e.g. yourname.chanora.chanoraFlutter); do
NOT commit that change back since it is user-specific.
This commit is contained in:
@@ -96,3 +96,6 @@ chanora-docs-repo-format-*.zip
|
||||
|
||||
# opencode agent local config
|
||||
opencode.json
|
||||
|
||||
# iOS framework build artifacts produced by chanora_bridge.podspec
|
||||
/apps/chanora_flutter/ios/Frameworks/
|
||||
|
||||
@@ -30,6 +30,16 @@ flutter_ios_podfile_setup
|
||||
target 'Runner' do
|
||||
use_frameworks!
|
||||
|
||||
# Chanora Rust bridge as a vendored framework. The podspec runs
|
||||
# `cargo build --release --target aarch64-apple-ios` and wraps the
|
||||
# produced dylib into chanora_bridge.framework. CocoaPods then
|
||||
# integrates the framework into Runner.xcodeproj with the
|
||||
# appropriate Embed & Sign build phase, so the Runner app ships
|
||||
# with the bridge inside its Frameworks/ directory and
|
||||
# flutter_rust_bridge can dlopen() it at runtime via FRB's
|
||||
# default `chanora_bridge.framework/chanora_bridge` lookup path.
|
||||
pod 'chanora_bridge', :path => '.'
|
||||
|
||||
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||
target 'RunnerTests' do
|
||||
inherit! :search_paths
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||
PODS:
|
||||
- chanora_bridge (1.0.0)
|
||||
- connectivity_plus (0.0.1):
|
||||
- Flutter
|
||||
- Flutter (1.0.0)
|
||||
|
||||
DEPENDENCIES:
|
||||
- chanora_bridge (from `.`)
|
||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
chanora_bridge:
|
||||
:path: "."
|
||||
connectivity_plus:
|
||||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
chanora_bridge: 3346a0a77a8d5707cdcd9e5d9f490e281e1f428b
|
||||
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||
|
||||
PODFILE CHECKSUM: 251cb053df7158f337c0712f2ab29f4e0fa474ce
|
||||
PODFILE CHECKSUM: 15f58b0363434f244766f3301e00b9b1cdee096a
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
#
|
||||
# chanora_bridge — Rust bridge library as a CocoaPods-vended dynamic framework.
|
||||
#
|
||||
# What this file does:
|
||||
#
|
||||
# 1. `prepare_command` runs once per `pod install` and once per archive
|
||||
# / device build. It:
|
||||
# a) Invokes `cargo build --release --target <iOS arch>` for both
|
||||
# aarch64-apple-ios (device) and aarch64-apple-ios-sim
|
||||
# (simulator on Apple Silicon) so the resulting framework
|
||||
# works for both targets.
|
||||
# b) Wraps the built dylib in a proper `chanora_bridge.framework`
|
||||
# bundle (Versions/A layout with the right Info.plist).
|
||||
# c) Rewrites LC_ID_DYLIB to
|
||||
# @rpath/chanora_bridge.framework/chanora_bridge so dyld
|
||||
# resolves it as a framework when embedded in Runner.app.
|
||||
#
|
||||
# 2. `vendored_frameworks` exposes the result to the Pods workspace.
|
||||
# CocoaPods then integrates the framework into Runner.xcodeproj
|
||||
# with the appropriate embed-and-sign build phase.
|
||||
#
|
||||
# Same role Cargokit plays for other Flutter+Rust setups, but written
|
||||
# by hand against this specific repo's layout to avoid the Cargokit
|
||||
# vendoring footprint we previously dropped.
|
||||
#
|
||||
# Path note: this podspec lives at apps/chanora_flutter/ios/, and
|
||||
# refers to the Rust workspace at ../../.. (repo root). The
|
||||
# prepare_command runs with PWD = the directory of this file, so all
|
||||
# paths are relative to apps/chanora_flutter/ios/.
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'chanora_bridge'
|
||||
s.version = '1.0.0'
|
||||
s.summary = 'Chanora Rust bridge (libchanora_bridge) as an iOS framework.'
|
||||
s.description = <<-DESC
|
||||
Vendors the Rust-built libchanora_bridge as chanora_bridge.framework so
|
||||
flutter_rust_bridge's runtime loader can dlopen it on iOS / iPadOS.
|
||||
DESC
|
||||
s.homepage = 'https://github.com/EdisonJwa/chanora'
|
||||
s.license = { :type => 'Apache-2.0 OR MIT', :text => 'See LICENSE-APACHE / LICENSE-MIT at the repo root' }
|
||||
s.author = { 'EdisonJwa' => 'me@edison.network' }
|
||||
s.source = { :path => '.' }
|
||||
s.platform = :ios, '13.0'
|
||||
|
||||
# Build the Rust bridge on `pod install`. The script runs under
|
||||
# bash; we use `set -e` so any failure (cargo missing, target not
|
||||
# installed, link error) aborts the install loudly. The
|
||||
# CMAKE_POLICY_VERSION_MINIMUM + IPHONEOS_DEPLOYMENT_TARGET
|
||||
# env vars satisfy audiopus_sys's cmake invocation on modern
|
||||
# CMake 4.x and Apple iOS SDK; both are required.
|
||||
s.prepare_command = <<-SCRIPT
|
||||
set -e
|
||||
REPO_ROOT="$(cd ../../.. && pwd)"
|
||||
BRIDGE="$REPO_ROOT/target/aarch64-apple-ios/release/libchanora_bridge.dylib"
|
||||
|
||||
echo "[chanora_bridge.podspec] cargo build aarch64-apple-ios"
|
||||
cd "$REPO_ROOT"
|
||||
PATH="$HOME/.cargo/bin:$PATH" \\
|
||||
IPHONEOS_DEPLOYMENT_TARGET=13.0 \\
|
||||
CMAKE_POLICY_VERSION_MINIMUM=3.5 \\
|
||||
CMAKE_OSX_DEPLOYMENT_TARGET=13.0 \\
|
||||
cargo build --release --target aarch64-apple-ios -p chanora_bridge
|
||||
|
||||
if [ ! -f "$BRIDGE" ]; then
|
||||
echo "ERROR: bridge dylib not found at $BRIDGE" >&2
|
||||
exit 1
|
||||
fi
|
||||
cd "$REPO_ROOT/apps/chanora_flutter/ios"
|
||||
|
||||
# Build the chanora_bridge.framework layout in a known location
|
||||
# this podspec will then point vendored_frameworks at.
|
||||
FW=Frameworks/chanora_bridge.framework
|
||||
rm -rf "$FW"
|
||||
mkdir -p "$FW"
|
||||
cp "$BRIDGE" "$FW/chanora_bridge"
|
||||
|
||||
cat > "$FW/Info.plist" <<PLIST
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleExecutable</key><string>chanora_bridge</string>
|
||||
<key>CFBundleIdentifier</key><string>app.chanora.bridge</string>
|
||||
<key>CFBundleName</key><string>chanora_bridge</string>
|
||||
<key>CFBundlePackageType</key><string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key><string>1.0.0</string>
|
||||
<key>CFBundleVersion</key><string>1</string>
|
||||
<key>CFBundleSupportedPlatforms</key><array><string>iPhoneOS</string></array>
|
||||
<key>MinimumOSVersion</key><string>13.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
PLIST
|
||||
|
||||
install_name_tool -id "@rpath/chanora_bridge.framework/chanora_bridge" \\
|
||||
"$FW/chanora_bridge"
|
||||
echo "[chanora_bridge.podspec] framework ready at $FW"
|
||||
SCRIPT
|
||||
|
||||
# Pod CocoaPods picks this up; the framework gets embedded into
|
||||
# Runner.app/Frameworks/ with Embed & Sign automatically.
|
||||
s.vendored_frameworks = 'Frameworks/chanora_bridge.framework'
|
||||
|
||||
# The pod has no Objective-C sources; it's purely a framework
|
||||
# carrier. Suppress CocoaPods's source-file warning.
|
||||
s.source_files = 'Frameworks/chanora_bridge.framework/Headers/*.h'
|
||||
|
||||
# No public Swift / ObjC API to expose — the Flutter app talks to
|
||||
# the framework via FRB's FFI symbol lookup at runtime, not via
|
||||
# imports.
|
||||
end
|
||||
Reference in New Issue
Block a user