From a93109ac37572f624d8f632cb293bc3c81a5a643 Mon Sep 17 00:00:00 2001 From: EdisonJwa Date: Sat, 16 May 2026 17:31:43 +0800 Subject: [PATCH] fix(ios): rebuild chanora_bridge.framework on every Xcode build, not just pod install The podspec's prepare_command only fires on `pod install`. Once a framework was generated, Rust source changes were silently ignored because Xcode kept re-bundling the stale framework into Runner.app. Manifested today as: ran `cargo build --release --target aarch64-apple-ios` to pick up the lenient voice_join fix, ran `flutter build ios`, but Runner.app/Frameworks/chanora_bridge. framework/chanora_bridge was still the framework from the previous pod install (16:10) not the just-built 18:30 dylib. Add an explicit `script_phase` to the podspec that re-runs: 1. cargo build --release --target aarch64-apple-ios -p chanora_bridge 2. cp dylib into Frameworks/chanora_bridge.framework/chanora_bridge 3. install_name_tool -id @rpath/... on every Xcode 'Build', not just on pod install. The script short- circuits when the framework's binary mtime is newer than the cargo output (fast no-op on incremental builds where Rust didn't change). Side effect: every Xcode build now invokes cargo, which can take ~5 s on a warm cache and ~1 min cold. This is the right trade-off because the previous behavior silently shipped stale Rust code. --- .../ios/chanora_bridge.podspec | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/apps/chanora_flutter/ios/chanora_bridge.podspec b/apps/chanora_flutter/ios/chanora_bridge.podspec index b547c30..e844627 100644 --- a/apps/chanora_flutter/ios/chanora_bridge.podspec +++ b/apps/chanora_flutter/ios/chanora_bridge.podspec @@ -100,6 +100,52 @@ PLIST # Runner.app/Frameworks/ with Embed & Sign automatically. s.vendored_frameworks = 'Frameworks/chanora_bridge.framework' + # Re-run the cargo build + framework wrap on every Xcode build, + # not only on `pod install`. The `prepare_command` above runs + # once per `pod install` which is too sticky — Rust source + # changes were getting silently ignored because Xcode happily + # re-bundled the stale framework. This script_phase shells out to + # cargo on every Xcode "Build" so the framework is always in sync + # with the current Rust workspace. + # + # `:execution_position => :before_compile` runs the script before + # Xcode's CompileSources phase, so by the time the linker / embed + # step sees `Frameworks/chanora_bridge.framework`, it is fresh. + s.script_phase = { + :name => 'Rebuild chanora_bridge.framework from Rust', + :script => <<-SCRIPT, + set -e + REPO_ROOT="$(cd "${PODS_TARGET_SRCROOT}/../../.." && pwd)" + BRIDGE="$REPO_ROOT/target/aarch64-apple-ios/release/libchanora_bridge.dylib" + + echo "[chanora_bridge script_phase] 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 + + cd "$REPO_ROOT/apps/chanora_flutter/ios" + FW=Frameworks/chanora_bridge.framework + + # Skip the wrap step if the framework's binary is already + # up-to-date with the cargo output (fast no-op on incremental + # builds where Rust didn't change). + if [ -f "$FW/chanora_bridge" ] && [ "$FW/chanora_bridge" -nt "$BRIDGE" ]; then + echo "[chanora_bridge script_phase] framework already up-to-date" + exit 0 + fi + + mkdir -p "$FW" + cp "$BRIDGE" "$FW/chanora_bridge" + install_name_tool -id "@rpath/chanora_bridge.framework/chanora_bridge" \\ + "$FW/chanora_bridge" + echo "[chanora_bridge script_phase] framework refreshed" + SCRIPT + :execution_position => :before_compile, + } + # 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'