Swift #available keyword vs respondsToSelector

According to the pre-release Swift 2 documentation there is now an #available keyword that can be used with an if let or guard statement for checking API availability. It gives the following example:

let locationManager = CLLocationManager()
if #available(iOS 8.0, OSX 10.10, *) {
    locationManager.requestWhenInUseAuthorization()
}

Or

  • NSDateFormatter: Date according to currentLocale, without Year
  • How to implement an NSRunLoop inside an NSOperation
  • iphone: secure restfull server "The certificate for this server is invalid
  • Understanding branches in gcov files
  • ViewDidLoad / Unload messages to super?
  • POST data to a PHP method from Swift
  • let locationManager = CLLocationManager()
    guard #available(iOS 8.0, OSX 10.10, *) else { return }
    locationManager.requestWhenInUseAuthorization()
    

    Which it (the doc) states is the equivalent of the following Objective-C code:

    if ([CLLocationManager instancesRespondToSelector:@selector(requestWhenInUseAuthorization)]) {
      // Method is available for use.
    } else {
      // Method is not available.
    }
    

    Obviously respondsToSelector: only works on subclasses of NSObject whereas the #available keyword will work even for “pure” Swift code, so I appreciate that advantage.

    However since starting iOS development I’ve always been led to believe that the best practice for this situation is to detect the presence of an API rather than rely on the version it was introduced.

    As a more concrete example I’m thinking of when Apple introduced firstObject on NSArray in iOS 7 but retroactively made it available back to iOS 4 (where it was available, but private). Any code using respondsToSelector: would have worked on iOS < 7 but obviously a version check would fail.

    Are there any benefits to shifting to the #available keyword that I’ve missed?

    2 Solutions Collect From Internet About “Swift #available keyword vs respondsToSelector”

    A big advantage is that the Swift 2 compiler in Xcode 7 compares the
    availability of classes, methods, properties, … against the deployment
    target of your project.

    Using respondsToSelector was always error-prone. For Objective-C,

    if ([CLLocationManager instancesRespondToSelector:@selector(requestWhenInUseAuthorization)]) {
    

    the compiler only verifies if requestWhenInUseAuthorization is some
    known method, but it cannot verify if that check is logically correct.

    In Swift it is even worse because selectors can only be specified as
    strings and the compiler does not verify anything.

    With Swift 2/Xcode 7, the corresponding code

    let locationManager = CLLocationManager()
    if locationManager.respondsToSelector("requestWhenInUseAuthorization") {
        locationManager.requestWhenInUseAuthorization()
    }
    

    does not compile anymore if the deployment target is less than iOS 8,
    the error message is

    error: 'requestWhenInUseAuthorization()' is only available on iOS 8.0 or newer
                locationManager.requestWhenInUseAuthorization()
                                ^
    note: add 'if #available' version check
    

    With

    let locationManager = CLLocationManager()
    if #available(iOS 8.0, OSX 10.10, *) {
        locationManager.requestWhenInUseAuthorization()
    }
    

    the compiler knows that the method will only be called on iOS 8 and later
    and does not complain.

    respondsToSelector checks the the runtime to see if the selector is present. #available checks the published SDK to see if it’s included. In the WWDC video, they explicitly said that many public APIs start as private APIs in earlier OS versions. Which means in some cases it won’t be #available even though it respondsToSelector