Quick overview of how to use them
It’s always the same. A new iOS release means there are new APIs available to use in your project. But sometimes you do not have the option to raise the minimum supported iOS version, because this would reduce your user base, even if 50% of the users have already installed the new version just a month later.
📈 iOS 16 adoption report
Statistic from Mixpanel
But you don’t have to wait for such new features to be developed and released until you drop older versions of iOS. The #available
attribute helps us to add conditional checks for different iOS versions, and @available
allows us to mark certain classes or methods as only available for certain versions or platforms.
@available
to mark classes and methods
@available(platform-name version-number, *)
@available(swift version-number)
Use this annotation to make classes or methods only available for specific platforms.
@available(iOS 16.0, macOS 12.0, *)
struct DataTableView: View {
let data: [DataModel]
var body: some View {
// available of Table
// @available(iOS 16.0, macOS 12.0, *)
// @available(tvOS, unavailable)
// @available(watchOS, unavailable)
Table(data) {
...
}
}
}
and the usage of conditional checks with #available
helps us to use the new type in our code. Or with a fallback solution for older operating systems.
struct AppView: View {
@State var data: [DataModel] = [...]
var body: some View {
if #available(iOS 16, macOS 12.0, *) {
DataTableView(data: data)
} else {
DataStackView(data: data)
}
if #unavailable(iOS 16) {
// only visible on iOS 15 and lower
Text("Please upgrade to iOS 16 to have the newest features available.")
}
}
}
#unavailable
If you want to show users with old iOS versions a hint, that some features are not available, you can use the #unavailable
attribute.
if #unavailable(iOS 16) {
// only visible on iOS 15 and lower
Text("Please upgrade to iOS 16 to receive new updates")
}
deprecation of classes and methods
@available(*, unavailable, message: "Networking is no longer needed since iOS 16")
class Networking {}
With the unavailable
argument we can force developers to not use a class anymore. This will result in a compile error.
@available(iOS, deprecated: 16, obsoleted: 16.1, renamed: "DataTableView")
struct DataStackView: View { ... }
With the example above we can mark the DataStackView
as deprecated in iOS 16, which will throw a warning, and make it obsoleted with iOS 16.1. The rename argument gives other developers a hint of which view should be used instead (our new DataTableView
is available in iOS 16) and Xcode can fix it for them.
available platform values
The available attribute can not only limit the operating system version, you can also declare a specific swift version.
iOS
iOSApplicationExtension
macOS
macOSApplicationExtension
macCatalyst
macCatalystApplicationExtension
watchOS
watchOSApplicationExtension
tvOS
tvOSApplicationExtension
swift
You can also use the asterisk (*
) already seen above to indicate the availability of the declaration on all the platform names listed above.
@available(*, unavailable, message: "No longer needed")
But how can we add a property which type is only avaiable on a later release into a class which can not easily have an available attribute?
Let me show you an example. We have a ViewController
that is used in many places in the app. This controller should get a new iOS 17-only feature NewFeature
, but how can we add it to a class that needs to support earlier versions?
@available(iOS 17, *)
class NewFeature {
func newStuff() { /* calling iOS 17 only APIs */ }
}
// Deployment Target iOS 15
class ViewController: UIViewController {
// How can we use a property which is only available in iOS 17?
}
Protocol and optional feature
One solution I used is to create a protocol with the public APIs of the feature and have it as an optional lazy property inside the ViewController
. NewFeature
only needs to validate against this protocol.
protocol NewFeatureProtocol {
func newStuff()
}
@available(iOS 17, *)
extension NewFeature: NewFeatureProtocol {}
class ViewController: UIViewController {
// NewFeatureProtocol is available
lazy var newFeature: NewFeatureProtocol? = {
if #available(iOS 17, *) {
return NewFeature()
} else {
return nil
}
}()
}
We even have the possibility to implement a fallback solution which is available prior iOS 17.
Summary
In this article we have discussed the use of the @available
and #available
attributes to handle different iOS versions and platform-specific features.
Overall, these Swift attributes and conditional checks allow you to efficiently manage the availability of code features across different iOS versions and platforms efficiently.
Protocols can help to use these features as properties within other classes that cannot be increased in their available state.
Code snippets
if #available(iOS 16, *) {
print("iOS 16 and later")
} else {
print("iOS 15 or lower")
}
guard #available(iOS 16, *) else { return }
print("iOS 16 and later")
if #unavailable(iOS 16) {
// only visible on iOS 15 and lower
Text("Please upgrade to iOS 16 to receive new updates")
}
@available(*, unavailable, message: "Class unavailable")
@available(iOS, deprecated: 16, obsoleted: 16.1, renamed: "New method name")