Native iOS is the right call when the app depends on the phone rather than merely running on it — camera and sensors, background location, offline-first sync, widgets, biometrics, or a level of polish that users of this platform notice.
SwiftUI, with UIKit where it still wins
Most new screens are SwiftUI. Some are not: complex collection layouts, deeply custom transitions and a few older integrations are still cleaner in UIKit, and mixing the two is normal rather than a failure. We choose per screen and keep the seam explicit.
Data and sync
Offline behaviour is a design decision, not a library. What is cached, what is authoritative, what happens to a change made on a train — those answers are written down before the model layer is built, because retrofitting sync is one of the most expensive things you can do to a mobile app.
Release discipline
Signing, provisioning and TestFlight distribution run from the pipeline, not from a laptop. App Store review notes are written with the build. Privacy labels and permission strings are treated as part of the feature, since a rejected release costs a week.
Living with annual releases
Every autumn a new iOS lands and something changes. We keep deployment targets current, test on the beta before it ships, and avoid private behaviour that only works until it does not. An app that skips two OS cycles is a rebuild, not an update.
When we would say cross-platform instead
If both platforms need the same forms, lists and sync with little device integration, one codebase in React Native or Flutter will get you there faster and cheaper. The Android half of a native build is Kotlin; the comparison is argued on mobile development.