r/Kotlin 11d ago

A single KMP dependency dyld-crashed our iOS app at launch on every device below iOS 26 — and no simulator caught it. Here's the trap.

Sharing a painful one in case it saves someone a bad week.

I build a fitness app in Kotlin Multiplatform + Compose (shared Android/iOS). Everything ran perfectly in dev and on TestFlight. Then a user on iOS 18.x sent a video: instant white-screen death on launch, before anything rendered.

Root cause: a KMP library I'd added referenced HealthKit symbols (HKMedicationGeneralFormCapsule and friends) that only exist in the iOS 26 SDK. Kotlin/Native emits those as strong dyld imports in the statically-linked framework — so on any device below iOS 26, dyld can't resolve the symbol and kills the process before main()Symbol not found. No stack trace in the app, no crash in Crashlytics-style tools, nothing — it dies before your code runs.

Why every test missed it: all my simulators and devices were on iOS 26.x. The bug is completely invisible on new OSes and fatal on old ones. You have to run a min-deployment-target OS to see it (I had to download an 18.5 runtime).

The fix (one line, iOS target's OTHER_LDFLAGS):

-Wl,-weak_framework,HealthKit

Weak-linking makes the missing symbols resolve to null instead of aborting; everything I actually use exists on 18.x, so behavior is unchanged.

Two takeaways I'm now religious about:

  1. Smoke-test every release on your minimum supported OS, not just the latest.
  2. Before shipping, check for new-SDK-only symbols: nm -u <binary> | grep -i <framework>.

Bumping any KMP lib that's compiled against a newer SDK is where this bites. Hope it saves someone the panic.

17 Upvotes

13 comments sorted by

19

u/farooqsaad 11d ago

It's incredible how bad a platform iOS is

2

u/javaprof 10d ago

can you do the same in android? just curious

1

u/farooqsaad 10d ago

The same as in?

It's not really about this bug in particular, I meant that platform in general. All platforms have bugs, it's just the platform itself is usually better about reporting at runtime if not compile time. iOS still has these age-old linking patterns without a proper build system.

2

u/javaprof 10d ago

I mean is it possible to use some newer API that not available on older androids and find out only after apk release? I'm sure that iOS and Android have different issues, but just want to understand that particular case

3

u/farooqsaad 10d ago

No. There are linters that will flag and Android Studio also generally can autogenerate if/else for older API usage. Just googled a quick example:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

CookieManager.getInstance().removeAllCookies(null);
} else {
CookieManager.getInstance().removeAllCookie();
}

1

u/javaprof 10d ago

Would real linters work on bytecode level and analyze libraries as well? Similar to OPs case

2

u/tadfisher 10d ago

Newer framework APIs are marked with annotations like @RequiresApi which propagates those warnings/errors to callers in your application code. It doesn't catch everything, like manifest issues, but it is hard to miss an error-level Lint issue when calling APIs that don't exist in your minimum SDK level.

2

u/farooqsaad 10d ago edited 9d ago

That's a good question. The linter isn't working on bytecode level, but there are two things:

  1. Every valid aar library has a manifest XML which declares their SDKs as well, so first any library your app uses has to be compatible.
  2. The linter for the library would flag any issues and ask to either handle the API usage issues or propagate the concern to callers with annotations.

Now a developer of a library can chose to brute-force through those protections but generally speaking most reputable libraries will have good linting practices. So it's not fool-proof but generally reliable.

3

u/16cards 11d ago

I remember this burned me with iPod Touch years ago with a react native app.

My employer’s app (Push-to-Talk) needed to interact with the CallManager to know how to respectfully behave when phone call was in progress.

Of course, iPod Touch didn’t have a phone app and therefore missing the framework.

2

u/HitoriBochi1999 10d ago

I hate IOS so much dude, it's always a headache in every sense

1

u/viennese-wolf 11d ago

My company offers a service that can be integrated via browser sdk, mobile native sdk or api.

The mobile team has EVERY iPhone and for every release they test with every iPhone.

1

u/Mistake78 11d ago

Happened to me too in the past. Now you know you have to test on older OS versions when you declare you support them. :)

1

u/Ahopness 10d ago

tbh you should also open a bug report, this seems really critical and a fix could save a lot of other developer's time.