What does the * in #available(iOS 17, *) mean on tvOS, watchOS and visionOS, and how do you write a check that is correct on every platform?
Predict first
What does this print on the tvOS simulator?
import Foundation
func describe() -> String {
var parts: [String] = []
if #available(iOS 17, tvOS 17, watchOS 10, visionOS 1, macOS 14, *) { parts.append("new-enough") }
if #available(iOS 99, *) { parts.append("iOS99:true") } else { parts.append("iOS99:false") }
if #unavailable(iOS 99, tvOS 99, watchOS 99, visionOS 99, macOS 99) { parts.append("below-99-everywhere") }
return parts.joined(separator: " ")
}
print(describe())
- new-enough iOS99:true below-99-everywhere
- new-enough iOS99:false below-99-everywhere
- iOS99:false
Predict first
Is it accepted to write the star in an #unavailable condition?
import Foundation
if #unavailable(iOS 99, *) { print("x") }
- Yes
- No, compile error
Predict first
Does a call to a function marked unavailable on tvOS compile for tvOS?
import Foundation
@available(tvOS, unavailable) @available(watchOS, unavailable)
func pickDocument() {}
func use() { pickDocument() }
- Yes
- No, “unavailable in tvOS”
Mental model. A guest list that ends “and anyone else”: if your platform is not named, you are the anyone else and you are let in.
An availability condition lists platform and version pairs and ends with *, which stands for “any platform not listed, at the deployment target”. The consequence is that on a platform you did not list the condition is true. The check shows it: if #available(iOS 99, *) printed true when run on tvOS, because tvOS is not named. So if #available(iOS 17, *) { newAPI() } in a shared package says nothing about the Apple TV version; it always takes the new branch there, and if newAPI is newer than your tvOS deployment target the compiler will have already complained, so the practical failure is the opposite one, an iOS-only API used with the wrong version number on another platform.
List every platform the code builds for: #available(iOS 17, tvOS 17, watchOS 10, visionOS 1, macOS 14, *). Version numbers differ by platform, so look each up in the API’s documentation; since the 2025 releases all platforms share one year-based number (the simulators here all report major version 27), which makes the numbers match for new APIs but not for old ones (watchOS 10 and visionOS 1 correspond to iOS 17). For the opposite test use #unavailable(...), which always has an implicit *: writing it explicitly is an error (“platform wildcard '*' is always implicit in #unavailable”). To say that your own declaration does not exist somewhere, use @available(tvOS, unavailable), which turns any call from a tvOS build into a compile error: the supported way to stop a platform from using a feature, where #if only hides the code. Not run: behaviour on older OS versions than the simulators here (27.2).
iOS 99 is unlisted-platform territory on tvOS, so the starred check is true.
The wildcard is always implicit there.
The attribute turns the call into a compile error on that platform.
Common traps
- Writing
#available(iOS 17, *)in shared code and thinking it gates the tvOS version. On tvOS it is always true; name tvOS too. - Putting
*inside#unavailable. It is rejected because the wildcard is implicit.
They might follow up with
- When is
@available(..., unavailable)better than#if? When you want a call from the wrong platform to fail at compile time. - Does
if #availablechange the deployment target? No; it only gates the code at run time.
Related questions
- 20.1.1 How do you structure one codebase for several Apple platforms: shared cores, UI leaves, #if os, canImport and…
- 20.1.2 How do the platforms differ in input, process model and capabilities, and which entitlements and APIs change…
← What are os(), canImport() and targetEnvironment() actually true for… Which run-time probes identify the device (UIUserInterfaceIdiom… →