fix: enable Google ML Kit on Apple Silicon iOS 26+ simulators#862
Conversation
Apple removed Rosetta 2 from the default iOS 26 simulator runtime, which breaks `flutter run` for any project depending on Google ML Kit on Apple Silicon Macs. The published GoogleMLKit/* CocoaPods only ship arm64-iphoneos and x86_64-iphonesimulator slices and pin EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64, so the simulator build no longer finds a matching destination and dies with "Unable to find a destination matching the provided destination specifier". Until Google publishes proper arm64-iphonesimulator slices (https://issuetracker.google.com/issues/178965151), this change ships an opt-in Podfile helper under google_mlkit_commons that: 1. Re-labels the existing arm64 device slice of every ML Kit framework binary as iOS Simulator. Only the 4-byte LC_BUILD_VERSION.platform field is changed (2 -> 7), the same approach the well-known arm64-to-sim tool uses on closed-source SDKs. Idempotent. 2. Strips EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64 from the xcconfigs CocoaPods generates from the pod's user_target_xcconfig, so the user's app target is allowed to build for arm64-iphonesimulator. The example/ios Podfile is wired to call the helper from post_install, which lets the example app build, install and run on an Apple Silicon iOS 26.3 simulator. End-to-end validated: Text Recognition on the Text-From-Widget view returned the exact widget text. Device builds and release builds are not affected. Closes flutter-ml#825
The Ruby and Python entry points already have a brief header pointing at the README. Per-function docstrings were restating what the function name already said, and the Podfile inline comments duplicated the helper's self-documenting name.
|
Thanks for fix. |
…l_kit_flutter - PR flutter-ml#864 / Issue flutter-ml#863: Fix iOS IOSurface memory leak in pixelBufferToVisionImage. Replaces CoreImage CIContext+createCGImage round-trip with VisionImage(buffer:) via CMSampleBuffer wrapper, eliminating ~3.5 MiB leak per frame on sustained camera streaming. - PR flutter-ml#862 / Issue flutter-ml#825: Add Apple Silicon iOS 26+ simulator support. Ships opt-in Podfile helper (apple_silicon_simulator.rb + patch_arm64_simulator.py) that re-labels arm64 device slices as iOS Simulator and strips EXCLUDED_ARCHS. Updates README with usage instructions and wires up example app Podfile. - Issue flutter-ml#857: Fix IllegalStateException 'Reply already submitted' crash in DocumentScanner on Oppo/ColorOS devices. Clears pendingResult after calling .success() or .error() so duplicate onActivityResult deliveries are safe.
|
This PR is stale because it has been open for 14 days with no activity. |
|
It needs the release |
There was a problem hiding this comment.
Pull request overview
This PR adds an opt-in CocoaPods post_install helper to let apps using google_ml_kit_* build and run on Apple Silicon iOS 26+ simulators by (1) patching ML Kit vendored framework binaries to report the simulator platform and (2) removing the EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64 setting from pod-generated xcconfigs. This addresses the current upstream limitation where Google’s ML Kit pods ship no arm64-iphonesimulator slices and exclude arm64 on simulator, blocking native arm64 simulators without Rosetta.
Changes:
- Add a Python patcher that rewrites Mach-O
LC_BUILD_VERSION.platformfrom iOS to iOS Simulator inside ML Kit framework binaries underPods/. - Add a Ruby Podfile helper that runs the patcher during
pod installand stripsEXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64from generated.xcconfigfiles. - Document opt-in usage in
google_mlkit_commonsREADME and wire the example app’s Podfile to call the helper.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/google_mlkit_commons/README.md | Documents the opt-in Podfile helper and rationale for iOS 26+ Apple Silicon simulators. |
| packages/google_mlkit_commons/ios/scripts/patch_arm64_simulator.py | Implements the Mach-O/ar patcher for relabeling ML Kit binaries as simulator-compatible. |
| packages/google_mlkit_commons/ios/scripts/apple_silicon_simulator.rb | Adds a Podfile helper to invoke the patcher and remove the arm64 simulator exclusion line. |
| packages/example/ios/Podfile | Demonstrates usage by requiring the helper and calling it in post_install. |
| packages/example/ios/Podfile.lock | Updates lockfile checksum due to Podfile change. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@lucasdonordeste : resolve this as well: |
Odd-sized archive members are followed by a 1-byte padding so the next header starts on a 2-byte boundary. The rewrite loop skipped this byte on read but never re-emitted it on write, shifting every subsequent header and corrupting the framework binary whenever a member had an odd size. Applies the maintainer/Copilot-suggested fix verbatim.
|
@lucasdonordeste : could you take a look at the new comments |
Replace the one-shot pod-install relabel (which broke device builds until manually reverted) with a per-build Xcode script phase that relabels the arm64 slice's LC_BUILD_VERSION.platform to match the build target: iOS Simulator for simulator builds, iOS for device builds. The same pod install now works for both with no manual revert. - patch_arm64_simulator.py: edit the platform field in place via mmap (no lipo subprocess, no temp files, no marker); bidirectional 2<->7; bounds-checked Mach-O walk; fail loud when no framework binaries are found; tagged errors. - apple_silicon_simulator.rb: copy the patcher into Pods, strip the simulator arm64 exclusion via a robust key-based match, and install an always-out-of-date 'set -euo pipefail' build phase on each Pods aggregate target. - README: document the automatic per-build behavior and the full-revert steps.

Summary
Fixes #825. Lets projects depending on
google_ml_kit_*build, install and run on iOS 26+ simulators on Apple Silicon Macs without Rosetta 2.Why this is needed
Google's
GoogleMLKit/*CocoaPods only ship two slices per framework:arm64-iphoneosandx86_64-iphonesimulator. Their podspecs propagateEXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64to the consuming app viauser_target_xcconfig. On Apple Silicon Macs running iOS 26+ simulators (where Rosetta 2 is not available by default), the simulator only runsarm64, soflutter runfails with:Root cause is upstream in Google's
MLKitCommon/MLImage/MLKit*binary distribution — it must includearm64-iphonesimulatorslices. Tracked at https://issuetracker.google.com/issues/178965151. Until that lands, every consumer of these plugins is locked out of iOS 26 simulators.What this PR does
Adds an opt-in Podfile helper under
google_mlkit_commons/ios/scripts/that runs atpod installtime:patch_arm64_simulator.py— walks everyMLKit*/MLImage*framework binary insidePods/, finds thearm64slice (a BSDarstatic archive of Mach-O 64 objects), and changes a single 4-byte field per object:LC_BUILD_VERSION.platform, from2(iOS) to7(iOS Simulator). Same approach as the well-knownarm64-to-simtool. Idempotent — re-runningpod installis a no-op once binaries already report platform=Simulator.apple_silicon_simulator.rb— Ruby helper exposingmlkit_apple_silicon_simulator_patch(installer)for use from any consumer'sPodfile. Also stripsEXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64from the xcconfigs CocoaPods generates from each pod'suser_target_xcconfig.The example app's
Podfileis updated to call the helper frompost_install, andpackages/google_mlkit_commons/README.mddocuments the two lines a consumer needs to add to opt-in.Files touched:
packages/google_mlkit_commons/ios/scripts/patch_arm64_simulator.py(new)packages/google_mlkit_commons/ios/scripts/apple_silicon_simulator.rb(new)packages/google_mlkit_commons/README.md(docs section under iOS Requirements)packages/example/ios/Podfile(onerequire+ onemlkit_apple_silicon_simulator_patch(installer)call)packages/example/ios/Podfile.lock(auto-regen)Device builds, release builds, IPA builds — none of them are touched. The helper only runs when consumers add the two opt-in lines, and only modifies vendored binaries inside
Pods/plus pod-generated xcconfigs.Validation
End-to-end on this machine:
GoogleMLKit/*9.0.0,MLKitCommon14.0.0,MLImage1.0.0-beta8LC_BUILD_VERSIONand link as-is); 512 Mach-O objects relabeled totalSteps reproduced:
cd packages/example && flutter pub get && cd ios && pod install— patcher runs, prints[ml_kit] Patching 31 ML Kit framework(s) for Apple Silicon iOS Simulator...flutter run -d <iOS-26-sim-id>— Xcode build succeeds in ~38 s, no linker errors"This is sample text\nthat will be captured\nand processed using\nthe ML Kit Text Recognizer.\nTry different fonts\nand styles to test\nthe recognition capabilities!"), proving native MLKit inference runs correctly with the relabeled binaries.Runtime log noise observed: a
FaultforMLKITx_SRLRegistry: No binding was found for required, single-bound service: CCTPolicyVending_APIand anErrorforMLKITx_GIPPseudonymousIDStore: Shared App Groups unavailable— both are pre-existing Clearcut-telemetry / shared-storage warnings emitted by ML Kit in any debug build (sim or device); they do not block inference.Trade-offs and what's not validated
arm64-to-simapplies to other closed-source iOS SDKs. There is a non-zero risk that some MLKit code path expects an iOS-device-only system framework symbol that doesn't exist on the simulator. I only smoke-tested text recognition; barcode, face, pose, etc. weren't exercised. I'd appreciate maintainers' help validating other detectors before this is recommended for general use — that's why it's strictly opt-in and lives behind two Podfile lines.LC_BUILD_VERSION.platformfield in the load command isn't source-code extraction or algorithm analysis; Apple's ownvtoolships-set-build-versionfor exactly this purpose, andarm64-to-sim(582★) has done the same on closed Google SDKs for years without legal pushback. Maintainers may still want to make their own call here.google_mlkit_commonsversion or touch theCHANGELOG.md— happy to do either if preferred. Adding pure tooling underios/scripts/doesn't affect the published Dart/native runtime.exampleapp'sPRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)"is a pre-existing recursive value that breaksxcrun simctl install. To reproduce this PR's runtime smoke test you'll need to set a real bundle identifier locally first; the build itself succeeds either way. I deliberately left this out of the PR to keep scope narrow.How to reproduce on your machine
Test plan
google_mlkit_commonsversion