How to use @available attribute in Swift

Today I am going to write about attributes that Swift offers. Attribute is nothing but the construct which provides more information about declaration or type. As long as this blog post is concerned, I am going to keep the content specific to Declaration Attributes.

Attributes can be specified with @ symbol followed by attribute name and arguments. Arguments are specific only to certain attributes

For example,

@available(<attribute_name>)
@available(<attribute_name>)(<arguments>)

@available

Declaration attributes can only be applied to declarations. For example available is a declaration attribute which can be used to specify the Swift language version, platform or iOS version to which declaration belongs.

This attribute can be specified with two or more argument separated by comma. Argument list begins with one of the following platform names or language

  • iOS
  • iOSApplicationExtension
  • macOS
  • macOSApplicationExtension
  • watchOS
  • watchOSApplicationExtension
  • tvOS
  • tvOSApplicationExtension
  • swift

There is one distinction on how these two arguments can be used. If you specify asterisk (*) for platform names, it means declaration applied to all the platforms. However, you cannot generalize the Swift version specification with just *. You specifically have to mention the Swift version to which it applies.


@available(iOS 9, *)
func methodWhichUsesStackView() {

}
// Alternatively multiple OS versions
@available(iOS 9, macOS 10.10, *)

Not exactly related to this post, but you can also use attribute is you don't want to apply attribute to whole method


if #available(iOS 9, *) {
    // Use Native StackView
} else {
    // Use Third party library which mimics UIStackView
}

// For multiple platforms
#available(iOS 9, macOS 10.10, *)

Please remember to always end this list with * to encompass rest of the attributes even if your application is not targeting them

As mentioned earlier in post, you can also specify the Swift version to which the declaration applies

One caveat though, if you want to specify Swift version it can not go with platform availability attribute. Instead, you will have to specify two attributes on each line. One for Swift version and other for platform specification


@available(swift 3.0.2)
struct Swift3SpecificStruct {

}

// In combination with platform specification
@available(swift 3.0.2)
@available(iOS 9, macOS 10.11, *)
struct Swift3SpecificStruct {

}

Other arguments associated with @available attribute

unavailable

This can be used to specify if code is unavailable on certain platform or not. If specified a platform, construct or code cannot be used when that target is selected.

Message argument is optional, this can be used to hint the user to use alternative construct. Using message argument is highly recommended to make code more readable.


@available(macOS, unavailable, message: "We removed this class from Mac OS recently")
class iOSAvailable {

}

// OR
@available(iOS, unavailable, message: "We removed this class from iOS recently")
class macOSAvailable {

}

obsoleted and deprecated

Obsolete is used to make code unavailable and deprecated is just to raise warning that code is deprecated

// If you want code to be obsolete on iOS 9.1 and deprecated on 10.1
@available(iOS, deprecated: 10.1, obsoleted: 9.1, message: "API is deprecated in iOS 10.1 and obsoleted in 9.1")
class Available {

}

introduced

This argument specifies the language or version in which the routine was introduced. You cannot use the routine before introduced version number.

// Only available in targets pointing to iOS versions greater than 10.1
@available(iOS, introduced: 10.1)
class Available {

}

@discardableResult

Swift compiler usually throws warning when the result of method or function that returns value is unused.
For example,

func doIt() -> Int {
    return 100
}
// Following code will generate warning since result of method call doIt is unused
doIt()

// Following is one of the ways to suppress this warning without using @discardableResult attribute

_ = doIt()

Below is the way to use @discardableResult in order to suppress the warning generated by unused value returned by method returning value

@discardableResult func doIt() -> Int {
    return 100
}

// Below code will not generate any warning since we have used @discardableResult attribute
doIt()

@testable

This attribute is used when you want to import certain modules in your test class for unit testing. In general method and attributes declared as internal won't be available outside the module. However there are cases such as unit testing where constructs declared with internal need to be used outside the declared module.

However, if constructs are declared with more strict access modifiers (e.g. FilePrivate, Private) they won't be available outside the module

@testable import MyAwesomeApp
// All the internal method and properties declared inside MyAwesomeApp will be available to unit test

class AwesomeAppTests: XCTestCase {
    func testFunctionality() {
        // Testing code
    }
}

@objc

This attribute is used when constructs (classes, non-generic enums, protocols) in Swift are needed to be made available in Objective-C code. Classes that are marked with @objc need to be inherited from NSObject. Also, compiler automatically applies @objc attribute to any class that inherits from class marked with attribute @objc.

Also, protocols marked with @objc cannot inherit from protocols without @objc. Enums marked with this attribute can also be used in Objective-C code. However, for enums converted from Swift to Objective-C enum type and name are concatenated.

// Swift class can be used in Objective-C
@objc
class swfitClass: NSObject {

}

// Case can be used in Objective-C
@objc
enum Vehicle {
    case Car
    case Bike
}

// Above enum becomes VehicleCar and VehicleBike when used in Objective-C

@nonobjc

This attribute is used to make construct unavailable in Objective-C. It overrides any previous @objc declaration applied to construct.

Additionally, to quote Apple:

You use the nonobjc attribute to resolve circularity for bridging methods in a class marked with the objc attribute, and to allow overloading of methods and initializers in a class marked with the objc attribute

One major connection between @nonobjc and @objc methods is that methods marked with @nonobjc cannot override methods marked with @objc, while methods marked with @objc can override methods marked with @nonobjc.

Summary:

  • You can specify the declaration’s availability on different platforms and versions of Swift on the single line

  • The declaration that the available attribute applies to is ignored if the attribute specifies a platform or language version that doesn’t match the current target

  • If you use multiple available attributes, the effective availability is the combination of the platform and available Swift versions

References: