Swift @available, #available or #unavailable

Updated Jan 11, 2024#swift#attributes#directives

Swift provides first-class constructs for dealing with API changes, deprecate some old APIs to make room for the new ones. Designed directly into the language, they work with the compiler and with you to streamline the process of adding cross-platform and version compatibility to your project.

Annotate with @available

Apply this attribute to indicate a declaration’s life cycle relative to certain platform version or Swift language version. You will get a compile error when trying to use a new API on an older version.

// (1) Platform availability
@available(iOS 16, *)
@available(iOS 16, macOS 13, *)

// (2) With introduced, deprecated, and/or obsoleted
@available(iOS, introduced: 16)
@available(macOS, introduced: 13)
@available(iOS, deprecated: 13, renamed: "foo")
@available(iOS, introduced: 10, deprecated: 13, message: "foo")

// (3) With unavailable
@available(iOS, unavailable, renamed: "foo")
@available(*, unavailable, renamed: "foo")

// (4) Swift version availability
@available(swift 5.7)
@available(swift, deprecated: 5.6, message: "Deprecated in 5.6")

You use this attribute with following platform/language name:

- swift
- iOS, macOS, macCatalyst, watchOS, tvOS
- iOSApplicationExtension
- macOSApplicationExtension
- macCatalystApplicationExtension
- watchOSApplicationExtension
- tvOSApplicationExtension

(1) -> You provide a list of comma-separated platforms that you want and end with an asterisk (*) which is required to handle potential future platforms. When an API is marked as available in iOS, it’s implicitly marked available on tvOS and Mac Catalyst, because both of those platforms are derivatives of iOS.

(4) -> If you want to support development of your app with previous versions of Xcode or for your Swift package to work for multiple Swift compiler toolchains, you can use the @available attribute to annotate declarations containing new language features.

(3) -> If necessary, you can explicitly mark these derived platforms as being unavailable with additional @available attributes. The unavailable availability overrides all other availability information.

@available(iOS 16, *)
@available(tvOS, unavailable)
@available(macCatalyst, unavailable)
class Foo {
    //...
}

(1)/(2) + (4) -> You can apply multiple available attributes on a single declaration to specify the declaration’s availability on different platforms and different versions of Swift.

@available(swift 5.7)
@available(iOS 16, macOS 13, *)
class Foo {
    //...
}

(4) -> Only unavailable and deprecated are supported for Swift availability.

Conditionalize with #available or #unavailable

Swift historically supported the #available condition to check if a specific symbol is available for usage.

if #available(iOS 16, *) {
    // Do something with iOS 16 onwards
} else {
    // Fallback on earlier versions
}

Because the availability condition is not parsed as an expression, it cannot be negated with regular boolean operations (!/== false). The way instead is to make use of the else clause, but as unavailability checks are not interested at all in the positive portion of the check, doing so will leave behind an empty if branch.

if #available(iOS 16, *) {
    // no-op
} else {
    // if NOT in iOS 16, doSomething.
    doSomething()
}

Swift 5.6 introduced #unavailable condition to improve the readability of the unavailability check. A negative availability condition might also be necessary in cases where an API is marked as deprecated (and documented as non-functional) in newer OS versions.

if #unavailable(iOS 16, *) {
    doSomething()
}