Skip to content

fix(ios): keep text recognition switch exhaustive under selective install - #23

Merged
pedrol2b merged 3 commits into
developfrom
fix/ios-selective-text-recognition-build
Aug 20, 2026
Merged

pedrol2b merged 3 commits into
developfrom
fix/ios-selective-text-recognition-build

Conversation

@pedrol2b

Copy link
Copy Markdown
Owner

Related to #22 (intentionally not using a closing keyword — see Notes).

Summary

Text recognition failed to build on iOS as soon as any single language model
was disabled through the selective ML Kit install, which is the entire point
of that feature. Reported against 2.0.0 with an Expo prebuild config, but the
bare Podfile path produces identical flags and fails the same way.

Root cause

DomainTextRecognitionLanguage declares all five cases unconditionally, while
every case in TextRecognitionServiceFactory.createTextRecognizer was
individually wrapped in its own #if MLKIT_TEXT_RECOGNITION* flag with no
default:. Disabling a language compiled its case out while the enum still
declared it, so the switch was no longer exhaustive:

TextRecognitionServiceFactory.swift:31:7: error: switch must be exhaustive

Only all-five-enabled compiled, or all-five-disabled (where
MLKIT_TEXT_RECOGNITION_ANY goes unset and the whole file drops out). Every
partial selection — the feature's actual purpose — was broken.

This shipped because no iOS CI job compiles a partial config: build-ios.yml
builds the example app, and example/ios/Podfile pins all five to true.

The fix

The #if guards move inside each case body, via a makeOptions(for:)
helper that returns nil for models excluded from the build. The switch lists
all five cases unconditionally, so it stays exhaustive under every flag
combination.

A language whose model was excluded now throws a descriptive error naming the
configuration key to enable, matching the IllegalStateException Android
already raises from its own factory. createTextRecognizer/create become
throwing, so HybridTextRecognizer.init throws and its single call site in
HybridVisionCameraMLKit uses try.

Alternatives considered and rejected

default: + fatalError (suggested in the issue) compiles, and Swift
emits no unreachable-default warning when all five are enabled. Rejected
because it crashes the process where Android throws a catchable exception for
the same misconfiguration, and because it permanently disables exhaustiveness
checking — verified by simulating a sixth enum case: the default: version
compiles silently, this version fails the build.

Gating the enum cases (the issue's alternative) does not work.
TextRecognitionLanguage+toDomain.swift maps the Nitro-generated
TextRecognitionLanguage — C++-backed, all five cases, marked DO NOT MODIFY —
so removing domain cases relocates the same error into the mapper. It also
would not surface at the JS/TS boundary, since that union is generated from the
Nitro spec and is independent of build flags.

Validation

Real pod install + xcodebuild per config, reading the emitted
SWIFT_ACTIVE_COMPILATION_CONDITIONS back from the generated xcconfig rather
than trusting the Podfile (the podspec merges over all-true defaults and does
not validate keys, so a typo would silently produce an all-enabled build).

Config Branch Scope Result
latin only develop pod scheme FAIL — switch must be exhaustive at 31:7
latin only this branch pod scheme PASS
chinese only, latin off this branch pod scheme PASS
all five this branch pod scheme PASS
latin only this branch full app PASS (links)

Rows 1 and 2 use an identical Podfile and identical emitted flags; the branch
is the only variable. Row 1 reproduces the reporter's error at the same file,
line, and column. Row 3 also confirms the podspec still pulls base
GoogleMLKit/TextRecognition for shared types when textRecognition => false.

Not covered: the runtime throw reaching JS. Low risk rather than unknown —
HybridVisionCameraMLKit.createTextRecognizer already threw a RuntimeError
for the disabled-feature case before this change, so that propagation path is
pre-existing and exercised.

Notes

…tall

TextRecognitionServiceFactory wrapped each `case` of `switch language` in
its own `#if MLKIT_TEXT_RECOGNITION*` flag with no `default:`. Disabling
any single language model compiled that case out while
DomainTextRecognitionLanguage still declared all five, so the switch was
no longer exhaustive and iOS failed to build with "switch must be
exhaustive". Every partial selection broke; only all-five-enabled or
all-five-disabled (where MLKIT_TEXT_RECOGNITION_ANY drops the whole file)
compiled, which defeats the point of selective install.

Move the `#if` guards inside each `case` body via a `makeOptions(for:)`
helper returning nil for models excluded from the build. The switch now
lists all five cases unconditionally, so it stays exhaustive under every
flag combination and adding a language still fails the build until it is
handled here — which a `default:` clause would have silently swallowed.

Unavailable languages now throw a descriptive RuntimeError naming the
configuration key to enable, matching the IllegalStateException Android
already throws from its own factory, instead of crashing via fatalError.
createTextRecognizer/create become throwing, so HybridTextRecognizer.init
throws and its only call site in HybridVisionCameraMLKit uses `try`.

Fixes #22
Records the fix under [Unreleased]; the version header gets stamped at
release time. Describes the failure mode (non-exhaustive switch under any
partial language selection) and the new behavior for languages excluded
from the build.
@pedrol2b pedrol2b self-assigned this Aug 20, 2026
@pedrol2b pedrol2b added platform: ios Specific to iOS type: bug Something is not working as expected labels Aug 20, 2026
Repository owner deleted a comment from ecc-tools Bot Aug 20, 2026
Repository owner deleted a comment from ecc-tools Bot Aug 20, 2026
@pedrol2b
pedrol2b merged commit 79e3117 into develop Aug 20, 2026
6 checks passed
@pedrol2b
pedrol2b deleted the fix/ios-selective-text-recognition-build branch August 20, 2026 03:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform: ios Specific to iOS type: bug Something is not working as expected

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant