Commit Graph
7 Commits
Author SHA1 Message Date
Edison Jwa 9d8a1f8fd1 fix(ios,macos): address PR #26 review findings
- ios/Runner.xcodeproj/project.pbxproj: Update RunnerTests TEST_HOST
  paths from Runner.app/Runner to Chanora.app/Chanora (target was
  renamed in prior commit but test config still pointed at old paths,
  breaking xcodebuild test).
- Cargo.toml: Move release DWARF flags from workspace [profile.release]
  into Apple-only podspec CARGO_PROFILE_RELEASE_* env vars so Android,
  Linux, Windows release builds stay lean (~10MB DWARF avoided).
- ios/Runner/Info.plist + macos/Runner/Info.plist: Flip
  ITSAppUsesNonExemptEncryption from false to true (Chanora ships
  ChaCha20-Poly1305 local storage + tsclientlib ECDH/AES-EAX voice
  channel encryption, not exempt under Apple export-compliance rules).
- scripts/verify_silero_exports.sh: Make slice-aware via lipo -archs
  loop + per-arch nm -arch invocation so universal macOS builds
  verify every architecture slice, not just whichever slice nm picks.
- .gitignore: Drop .omo/ and .playwright-mcp/ entries (scope leak;
  unrelated tooling state, not part of PR #26 archive-symbol concern).
2026-06-07 23:12:07 +09:00
Edison Jwa a589ac953f fix(ios,macos): preserve Silero @_cdecl exports across Xcode Archive
The Apple CoreML Silero VAD backend resolves six @_cdecl Swift symbols
via dlsym(RTLD_DEFAULT) at runtime in the Rust audio crate. Local
flutter build paths preserved those symbols, but Xcode Archive (the
path used for TestFlight and App Store uploads) silently stripped them
through two independent mechanisms, causing Rust to fall back to
WebRTC VAD on every shipped build.

Both stripping mechanisms are now neutralised:

* ld dead-strip: OTHER_LDFLAGS now whitelists each of the six
  chanora_silero_vad_* symbols via repeated `-Xlinker -exported_symbol`
  pairs in ios/Flutter/{Release,Debug}.xcconfig and
  macos/Flutter/Flutter-{Release,Debug}.xcconfig.
* install-time strip: STRIP_STYLE is set to `non-global` in the same
  four xcconfigs so the post-link strip phase no longer drops exported
  global text symbols from the Archive product. Cost: ~264 bytes per
  binary; verified `xcrun strip` vs `xcrun strip -x` behaviour.

Self-test wired into both AppDelegates: at launch on a utility queue,
ChanoraSileroSelfTest resolves all six symbols through dlsym (the same
path the Rust runtime uses, not a direct call that would mask the bug
class) and exercises create → reset → process → destroy. Result is
logged via NSLog and surfaces in Console.app / idevicesyslog.

A post-link verify_silero_exports.sh build phase runs nm -gU on the
final Archive binary and fails the build if any of the six symbols are
missing. Empirically caught the original Archive regression that
flutter build --no-codesign did not.

CocoaPods bridge podspecs now emit a proper .dSYM via dsymutil so
TestFlight crash reports are symbolicated; Cargo.toml release profile
sets `debug = true` because dsymutil needs DWARF in the input dylib.

macOS chanora_bridge.podspec PATH inserts /opt/homebrew/opt/rustup/bin
ahead of /opt/homebrew/bin so rustup's cargo (which has the
x86_64-apple-darwin target installed) wins over the homebrew rust
formula that is aarch64-only.

iOS Podfile target renamed from `Runner` to `Chanora` to match the
Xcode target name shipped in the project (the workspace and scheme
already referenced Chanora; the Podfile mismatch produced lint
warnings during `pod install`).

ITSAppUsesNonExemptEncryption=false declared in both Info.plist files
so TestFlight and App Store Connect uploads skip the export-compliance
prompt; Chanora uses only platform-provided TLS.

.gitignore now covers Xcode archive bundles, IPA exports, dSYM
directories, the local macOS release zip, and agent/tooling state
directories so generated TestFlight artifacts no longer appear in
git status.

End-to-end verified by headless archive:
  xcodebuild -workspace Runner.xcworkspace -scheme Runner \
    -configuration Release -destination 'generic/platform=iOS' \
    -archivePath /tmp/chanora.xcarchive archive CODE_SIGNING_ALLOWED=NO
nm -gU on the resulting .app/Chanora binary shows all six
chanora_silero_vad_* symbols present.
2026-06-07 23:12:07 +09:00
Edison Jwa cac178f4af build(ios): link local SileroCoreML package 2026-06-02 01:53:21 +09:00
Edison Jwa e8e9fa8ccf build(ios): remove onnxruntime pod wiring 2026-05-31 22:30:46 +09:00
Edison Jwa 6af4ecab0f feat(voice): add iOS VAD runtime support 2026-05-21 20:51:45 +09:00
EdisonJwa a93109ac37 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.
2026-05-16 17:31:43 +08:00
EdisonJwa 1f729284b3 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.
2026-05-16 15:57:36 +09:00