feat: self-contained iOS MLKit — zero consumer setup (v4.15.1)
The root problem with v4.15.0's MLKit shipment was that downstream apps
had to: (1) add -ObjC to OTHER_LDFLAGS, (2) copy MLKit resource bundles
into the app bundle via an Xcode build phase, (3) force the KMP framework
to be dynamic. This PR eliminates all three consumer-side steps.
How:
1. `-ObjC` propagation — the cinterop mlkitAccurate.def already carries
`linkerOpts = -ObjC`, which Kotlin/Native applies at the consumer's
framework link step. For the KMP default (dynamic framework), this
preserves MLKit's ObjC class/category metadata inside the resulting
dylib; the consumer's app-link step just embeds the dylib, so it
doesn't need -ObjC of its own. No change needed in consumer
OTHER_LDFLAGS.
2. Resource bundles — MLKit's .tflite / .binarypb weights (~9 MB) are
now staged (Gradle `stageMlkitResources` task) into the library's
iosMain Compose Multiplatform resource set. Compose packages them
into the consumer's iOS app bundle under
`compose-resources/composeResources/...generated.resources/files/mlkit/`.
At first MlKitPose.detect() call, the new
`MlKitResourceBootstrap.ensureInitialized()` runs once — reads the
bundled bytes via `Res.readBytes`, writes them to
`NSCachesDirectory()/MLKitResources/<BundleName>.bundle/<file>`,
and registers that path with a small NSBundle swizzle.
3. NSBundle swizzle — `src/nativeInterop/mlkitRedirect/MLKitResourceRedirect.m`
swizzles `-[NSBundle URLForResource:withExtension:]` at +load time.
When MLKit's internal lookup for its three resource bundles
(MLKitPoseDetectionAccurateResources / MLKitPoseDetectionCommonResources /
MLKitXenoResources) falls through the main bundle, the swizzle
redirects to the Caches-directory copy the Kotlin bootstrap wrote.
Compiled to libMLKitRedirect.a via the new `compileMlkitRedirect`
Gradle task and folded into the MLKit cinterop .def's
staticLibraries list.
4. Dynamic framework — KMP's default is dynamic, so no consumer action
is required unless they explicitly set `isStatic = true`. The
sample iosApp's composeApp/build.gradle.kts is simplified to the
default dynamic config, and the Xcode iosApp target's OTHER_LDFLAGS
`-ObjC` entry + "Copy MLKit Resource Bundles" shell-script build
phase are removed — proving the library is self-contained.
Result: downstream consumers (kima) can bump from 4.14.0 → 4.15.1 with
zero other changes and MLKit pose detection works on iOS.
Version bumped to 4.15.1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>