How to handle API Changes with #available

⋅ 4 min read ⋅ Swift

Table of Contents

Every year Apple introduces new features to the system, and sometimes they have to deprecate some old APIs to make room for the new ones. Change is an inevitable thing in programming.

One of the tools that cope with the change in iOS is Availability Condition.

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

What is Availability Condition

Typically, You will get a compile error when using a new API on an older version of iOS.

You will get a compile error when trying to use a new API on an older version of iOS.
You will get a compile error when trying to use a new API on an older version of iOS.

An availability condition can perform a runtime platform version checking based on specified arguments.

An availability condition has the following form:


if #available(platform name version, ..., *) {
    // statements to execute if the APIs are available
} else {
    // fallback statements to execute if the APIs are unavailable
}

Here is some examples of #available syntax.

if #available(iOS 15, *) {
// iOS 15 and no restriction on other platforms.
}

if #available(iOS 13.1, *) {
// iOS 13.1 and no restriction on other platforms.
}

if #available(iOS 15, macOS 11, *) {
// iOS 15, macOS 11, and no restriction on other platforms.
}

The compiler uses the information from the availability condition to verify that the APIs in that block of code are available.

This makes a compiler happy and silent about the error that we received earlier.

The compiler uses the information from the availability condition to verify that the APIs in that block of code are available.
The compiler uses the information from the availability condition to verify that the APIs in that block of code are available.

Here is an example of using an availability condition to use a new iOS 15 API.

override func viewDidLoad() {
super.viewDidLoad()

if #available(iOS 15, *) {
print("New iOS 15 API")
} else {
print("Old API")
}
}

Where we can use Availability Condition

An availability condition can be used only in if, while, and guard statements.

You can't treat it like a normal boolean of function. The following samples are prohibited.

let iOS15 = #available(iOS 15, *)

if #available(iOS 15, *) == false {}

if #available(iOS 15, *) || isProMember {}

if #available(iOS 15, *) && isProMember {}

When should we use Availability Condition

As mentioned earlier, an availability condition is a tool for handling changes in an API. So I will use changes that we usually see as examples.

Brand new feature

When there is a brand new API that does not exist in old versions, we can adopt it with #available.

In iOS 13, we have a new dark mode supported in iOS. We can use an availability condition to change our app to a dark appearance for users with new iOS without affecting old users.

override func viewDidLoad() {
super.viewDidLoad()

if #available(iOS 13, *) {
overrideUserInterfaceStyle = .dark
}
}

New and better alternative

Some API is just an improvement over the old one. We have to keep two separate implementations in an if-else block in this kind of change.

In iOS 15, we have a new way to set up a button (let's assume it is a better alternative). An availability condition allows us to adopt it right away.

if #available(iOS 15, *) {
var configuration = UIButton.Configuration.filled()
configuration.title = "Click Me"
let button = UIButton(configuration: configuration)
view.addSubview(button)
} else {
let button = UIButton(type: .system)
button.setTitle("Click Me", for: .normal)
view.addSubview(button)
}

As mentioned earlier, we need to keep two implementations for these changes. And we probably need to maintain them for a year before we can change the minimum iOS version and ditch the old implementation.

The only benefit of this kind of adoption is you won't forget that there is a new API and can adopt it right away once you ditch the old version. But I think you have to judge for yourself whether it is worth your maintenance cost for this kind of change.

New approach/Deprecated API

Apple comes up with a new approach to doing things from time to time. When this happens, an old API is usually marked deprecated.

We also need to maintain two implementations in this case, but it is more convincing since the old API is deprecated.

In iOS 14, Apple change the method signature of requestReview().

if #available(iOS 14.0, *) {
if let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene {
SKStoreReviewController.requestReview(in: scene)
}
} else if #available(iOS 10.3, *) {
SKStoreReviewController.requestReview()
}

The deprecated method usually works in newer versions, but you shouldn't rely on that. Even though we need to maintain two versions of code like the last changes that we discussed, I encourage you to do so in this case.

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Conclusion

In this article, we learn a tool to live with the changes. I only cover only half of the story here.

We have another tool (@available) to help us with this. I will cover that in another post and point out a scenario where we need that.


Read more article about Swift or see all available topic

Enjoy the read?

If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron Buy me a coffee Tweet Share
Previous
How to use async/await in Flutter

Many things can go wrong when you write an asynchronous function in Dart. Let's learn how to use Future, async, and await properly.

Next
How to create observable variables from other observable variables in GetX

Learn how to make derived variables observable in GetX.

← Home