What do the deployment target, availability checks and required device capabilities decide, and how do you choose which OS versions to support?
Predict first
With a deployment target of iOS 17, a SwiftUI Tab (new in iOS 18) is used with no availability check. Does it compile?
import SwiftUI
struct V: View {
var body: some View {
TabView { Tab("Home", systemImage: "house") { Text("Hello") } }
}
}
- No: it is only available in iOS 18 or newer
- Yes, the newest SDK is enough
- Yes, with a warning
Predict first
The same code with an #available(iOS 18, *) guard and an older fallback?
import SwiftUI
struct V: View {
var body: some View {
if #available(iOS 18, *) {
TabView { Tab("Home", systemImage: "house") { Text("Hello") } }
} else {
TabView { Text("Hello").tabItem { Label("Home", systemImage: "house") } }
}
}
}
- Compiles
- Still fails
Mental model. The SDK is the newest tool catalogue; the deployment target is the oldest workshop you promised to serve. A tool from the new catalogue needs a plan B for workshops that lack it.
Two versions matter in every build. The SDK you compile against (the newest, from the Xcode you use) decides which APIs exist for the compiler; the deployment target (IPHONEOS_DEPLOYMENT_TARGET, recorded as the minimum OS version) decides the oldest system your app will run on. The gap between them is where bugs live: an API newer than your deployment target is not guaranteed to exist at run time, so the compiler makes you guard it with if #available(iOS 18, *) (or mark your own code @available), and an unguarded use is a compile error, as the first check below shows, while the guarded version compiles with an older fallback. Using a new API without a guard would crash old devices at launch or when the code runs, which is why the compiler enforces it. The same applies to SwiftUI modifiers, UIKit methods and entire frameworks (weak-linked when newer than the target).
Choose the minimum supported OS from data: your analytics show the share of users per OS version, and Apple’s own distribution numbers show the market. A common policy is the current and the previous one or two major versions, balancing reach against the cost of fallback code and the testing matrix; raising it lets you delete availability checks and adopt new APIs, lowering it adds reach and effort. Announce increases (older users stay on the last compatible version of your app, which the App Store can serve). UIRequiredDeviceCapabilities in Info.plist (for example arm64 or camera-flash) tells the App Store which devices can install the app: list a capability only if the app truly cannot function without it, because it removes devices from your audience, and handle optional hardware at run time instead (a missing camera or Face ID should degrade gracefully). Keep the app’s size in view too (Q439) because it is per device. Review these settings at every major OS release as part of the release train: add the new SDK, build, fix deprecations, and decide whether to move the floor.
The compiler enforces availability against the deployment target, so new APIs need a guard.
The guard tells the compiler the new API is only used where it exists, and the fallback covers the rest.
Common traps
- Using a new API without an availability check. It is a compile error, or a crash on older systems if forced through.
- Declaring a required device capability “just in case”. It silently removes devices from your App Store audience.
They might follow up with
- How do you pick the minimum OS? From your user analytics and the cost of supporting older versions, usually the current plus one or two earlier majors.
- What does
#availabledo? Checks the OS version at run time so new APIs are only called where they exist.
Related questions
- 25.5.1 How do you make schema, API and data changes backward compatible: expand and contract, tolerant readers…
- 25.1.2 What is the difference between a capability, an entitlement and an Info.plist key, and what breaks when they…
- 25.4.2 How do you run a release train for many apps: cadence, branch cuts, ownership, go/no-go criteria, hotfix…
- 6.7.1 What is Info.plist for, and which keys matter most?
- 21.7.1 What is app thinning, how do on-demand resources and Background Assets reduce install size, and what happened…
← How do you make schema, API and data changes backward compatible…