[swift-evolution] #available has a huge anti-pattern.

Devin Coughlin dcoughlin at apple.com
Thu Feb 4 15:04:34 CST 2016


James,

We did consider an availability model with feature detection similar to the one you proposed. This is essentially the model that Objective-C has. We’ve found that there are significant drawbacks to that model and believe that version checks + compile-time checking offer a better user experience.

* Compile-time checking of availability makes version checking less fragile.

A key component of Swift’s availability model is that — unlike in Objective-C or JavaScript — any use of an API element is verified for availability by the compiler. This eliminates the main source of fragility in the version checking model: getting a version check wrong and calling the API anyway. The compiler makes the strong guarantee that it will detect this and inform the developer that they have made a mistake.


* Feature detection checks are hard to reason about and test.

With version checks it is is immediately obvious on what environment the code will execute. This makes it easy to reason about, for example, how to test the given class/method/branch. In contrast, with feature detection even determining when a given branch of an availability check will execute requires a trip to the documentation. A polyfill model would make this even more challenging because the feature could be available earlier than the documentation states!


* SPI and direct feature detection. 

Apple sometimes adds features first as SPI and only later makes them available as API, potentially changing behavior when doing so. This means that a naive check for the presence of, say, a class or method, does not necessarily make it safe to call. For example, Apple added UINib as API in iOS 4, but earlier versions used an incompatible class with the same name. In this case, the Objective-C idiom checking for the feature effectively “lied” about UINib’s availability. When it comes to API, the run-time checks for #available need to allow symbols to be present but still not considered available. Version checks (either OS versions or library versions) are one mechanism to do this.


* Dead check detection

One nice aspect of version checks is that it gives the compiler the ability to detect availability checks that are no longer needed because the application is being deployed only on versions of the OS on which the check would always succeed. This is not possible with a direct feature detection model alone (it still needs APIs to be annotated with their versions) and would help prevent older codebases from being littered with feature checks falling back to effectively dead code that no one knows whether it is safe to remove or not.


* Overloading makes it verbose to check for a feature.

Swift’s support for function and method overloads makes it quite verbose to check for existence of a feature. Unambiguously identifying a method in a #available check would require specifying its parameter types and even constraints on generic type parameters.


* Features are often correlated.

In Objective-C codebases we find that developers often make implicit assumption about OS versions even when using direct feature checks. For example, a developer will often check for the existence of one method and then perform an unguarded use of another method that was introduced in the same version. With a direct feature detection model, the developer would have to check for each of these features separately — even though they know they were introduced in the same OS version! The fact that Objective-C developers are already skipping checks for correlated features shows that their mental model is already based on versions, so it makes sense to match that model in the affordances exposed by the language.


All that said, polyfill is a widely used technique and it would be interesting to see what it would take to *safely* integrate into Swift. In my view, the two key challenges there are (1) compatibility (whose polyfill wins when two libraries try to polyfill the same thing?) and (2) how to make assume-guarantee @available() annotations with polyfill/feature detection safe at compile time and efficient without leaking implementation details to clients (we don’t want every function to have to list all the functions it calls [and all the functions they call, etc.] in its @available() annotation!).


Devin



> On Feb 2, 2016, at 2:03 AM, James Campbell via swift-evolution <swift-evolution at swift.org> wrote:
> 
> Coming from a web background (before my iOS career) to me #avaliable has huge problem. It encourages fragility.
> 
> In my eyes we should encourage two types of detection: Features to make code more adaptable to different environments and language version detection: so we can understand the actual code.
> 
> See this example below:
> 
> func magic(object: Object)
> {
>   if(#avaliable(9.0, 10))
>  {
>   object.foo()
>  }
> }
> 
> Ideally for me I would love to check if the foo function exists like so:
> 
> func iOS9OnlyProtocolFunction(object: Object)
> {
>   if(#avaliable(Object.foo))
>  {
>     object.foo()
>  }
> else 
> {
>   object.baz()
>  }
> }
> 
> I think this encourages feature detection which results in less fragile code. What I would love to do is also to extend this to extensions so we could encourage polyfills.
> 
> extend object where not_avaliable(Object.foo) 
> {
>   func foo() 
>  {
>    //Polyfill for platforms which don't support the Object.foo method
>  }
> }
> 
> Not sure about compiler details but being able to polyfill the function results in much cleaner code for me. I love this approach from the web, so I created my own Objective-C Library to do this:
> 
> https://github.com/jcampbell05/Polly <https://github.com/jcampbell05/Polly>
> ___________________________________
> 
> James⎥Lead Engineer
> 
> james at supmenow.com <mailto:james at supmenow.com>⎥supmenow.com <http://supmenow.com/>
> Sup
> 
> Runway East
> 
> 
> 10 Finsbury Square
> 
> London
> 
> 
> EC2A 1AF 
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160204/132bae7c/attachment.html>


More information about the swift-evolution mailing list