Skip to content

fix: enable Google ML Kit on Apple Silicon iOS 26+ simulators#862

Open
lucasdonordeste wants to merge 2 commits into
flutter-ml:developfrom
lucasdonordeste:feat/apple-silicon-ios-simulator
Open

fix: enable Google ML Kit on Apple Silicon iOS 26+ simulators#862
lucasdonordeste wants to merge 2 commits into
flutter-ml:developfrom
lucasdonordeste:feat/apple-silicon-ios-simulator

Conversation

@lucasdonordeste
Copy link
Copy Markdown

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-iphoneos and x86_64-iphonesimulator. Their podspecs propagate EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64 to the consuming app via user_target_xcconfig. On Apple Silicon Macs running iOS 26+ simulators (where Rosetta 2 is not available by default), the simulator only runs arm64, so flutter run fails with:

Uncategorized (Xcode): Unable to find a destination matching the provided destination specifier

Root cause is upstream in Google's MLKitCommon/MLImage/MLKit* binary distribution — it must include arm64-iphonesimulator slices. 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 at pod install time:

  1. patch_arm64_simulator.py — walks every MLKit*/MLImage* framework binary inside Pods/, finds the arm64 slice (a BSD ar static archive of Mach-O 64 objects), and changes a single 4-byte field per object: LC_BUILD_VERSION.platform, from 2 (iOS) to 7 (iOS Simulator). Same approach as the well-known arm64-to-sim tool. Idempotent — re-running pod install is a no-op once binaries already report platform=Simulator.
  2. apple_silicon_simulator.rb — Ruby helper exposing mlkit_apple_silicon_simulator_patch(installer) for use from any consumer's Podfile. Also strips EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64 from the xcconfigs CocoaPods generates from each pod's user_target_xcconfig.

The example app's Podfile is updated to call the helper from post_install, and packages/google_mlkit_commons/README.md documents 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 (one require + one mlkit_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:

Mac Apple Silicon (M-series)
Xcode 26.2 (Build 17C52)
iOS Simulator iPhone 17 Pro / iOS 26.3.1 (23D8133), arm64 native
Flutter 3.x stable
ML Kit pods GoogleMLKit/* 9.0.0, MLKitCommon 14.0.0, MLImage 1.0.0-beta8
Pods patched 27 of 31 frameworks (the other 4 ship without LC_BUILD_VERSION and link as-is); 512 Mach-O objects relabeled total

Steps reproduced:

  1. 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...
  2. flutter run -d <iOS-26-sim-id> — Xcode build succeeds in ~38 s, no linker errors
  3. App installs and launches on the Apple Silicon iOS 26.3 simulator
  4. Tapped Vision → Text Recognition → Text From Widget Example → Capture and Recognize Text
  5. Recognition Result returned the exact widget text byte-for-byte ("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 Fault for MLKITx_SRLRegistry: No binding was found for required, single-bound service: CCTPolicyVending_API and an Error for MLKITx_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

  • This is the same hack arm64-to-sim applies 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.
  • Marked as opt-in, not as default behavior. Consumers who don't add the helper see no change.
  • The MLKit ToS prohibits "reverse engineer or attempt to extract the source code". Modifying a 4-byte LC_BUILD_VERSION.platform field in the load command isn't source-code extraction or algorithm analysis; Apple's own vtool ships -set-build-version for exactly this purpose, and arm64-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.
  • I did not bump google_mlkit_commons version or touch the CHANGELOG.md — happy to do either if preferred. Adding pure tooling under ios/scripts/ doesn't affect the published Dart/native runtime.
  • The example app's PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)" is a pre-existing recursive value that breaks xcrun 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

git fetch && git checkout feat/apple-silicon-ios-simulator
cd packages/example
flutter clean && flutter pub get
cd ios && pod install                     # should print "[ml_kit] Patching ..."
flutter build ios --simulator --debug      # links cleanly on iOS 26 sim
# To `flutter run` you'll also need a real PRODUCT_BUNDLE_IDENTIFIER set
# in Runner.xcodeproj as noted above.

Test plan

  • CI (existing) still green
  • Reviewer reproduces build on Apple Silicon iOS 26 simulator
  • (Stretch) Reviewer smoke-tests at least one detector beyond Text Recognition
  • Decision on whether to bump google_mlkit_commons version

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Projects including google_ml_kit do not run on iOS 26 simulator

1 participant