#available has a huge anti-pattern


(James Campbell) #1

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

···

*___________________________________*

*James⎥Lead Engineer*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *


Public API condition depending on swift version of user
(Joe Groff) #2

Polyfills have their own tradeoffs; they tend to encourage constant accretion of glue code as new versions get added if there's no pressure to drop old versions, leading to a significant amount of the multi-megabyte Javascript framework downloads we all complain about these days. That said, you might be able to use the related `@available` attribute to introduce backfill extensions that are only available on older systems.

-Joe

···

On Feb 2, 2016, at 2:03 AM, James Campbell via swift-evolution <swift-evolution@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
___________________________________

James⎥Lead Engineer

james@supmenow.com <mailto:james@supmenow.com>⎥supmenow.com <http://supmenow.com/>
Sup

Runway East >

10 Finsbury Square

London

> EC2A 1AF

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(James Campbell) #3

Awesome :slight_smile:

···

*___________________________________*

*James⎥Lead Engineer*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On Tue, Feb 2, 2016 at 4:26 PM, Joe Groff <jgroff@apple.com> wrote:

Polyfills have their own tradeoffs; they tend to encourage constant
accretion of glue code as new versions get added if there's no pressure to
drop old versions, leading to a significant amount of the multi-megabyte
Javascript framework downloads we all complain about these days. That said,
you might be able to use the related `@available` attribute to introduce
backfill extensions that are only available on older systems.

-Joe

On Feb 2, 2016, at 2:03 AM, James Campbell via swift-evolution < > swift-evolution@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

*___________________________________*

*James⎥Lead Engineer*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com/>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jean-Daniel) #4

Using function availability has proven fragile in the past too. A function may be present but private on older system, and have a slightly different behavior or crash, and so should not be used.

···

Le 2 févr. 2016 à 11:03, James Campbell via swift-evolution <swift-evolution@swift.org> a écrit :

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
___________________________________

James⎥Lead Engineer

james@supmenow.com <mailto:james@supmenow.com>⎥supmenow.com <http://supmenow.com/>
Sup

Runway East >

10 Finsbury Square

London

> EC2A 1AF

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Zachary Waldowski) #5

Joe —

Is this intended to be a supported behavior? I was all excited the other
day to discover this behavior, using it to backport iOS 9's
NSLayoutConstraint DSL. It mostly works, but as soon as an expression
gets relatively complex (array literals >5 or so) or has any sort of
error in it, Swift falls back to reporting an availability error.

Zach Waldowski zach@waldowski.me

···

On Tue, Feb 2, 2016, at 11:26 AM, Joe Groff via swift-evolution wrote:

Polyfills have their own tradeoffs; they tend to encourage constant
accretion of glue code as new versions get added if there's no
pressure to drop old versions, leading to a significant amount of the
multi-megabyte Javascript framework downloads we all complain about
these days. That said, you might be able to use the related
`@available` attribute to introduce backfill extensions that are only
available on older systems.

-Joe

On Feb 2, 2016, at 2:03 AM, James Campbell via swift-evolution <swift- >> evolution@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
*___________________________________*

*James⎥Lead Engineer*

*james@supmenow.com⎥supmenow.com[1]*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

_______________________________________________
swift-evolution mailing list swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_________________________________________________
swift-evolution mailing list swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Links:

  1. http://supmenow.com/


(Devin Coughlin) #6

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@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
___________________________________

James⎥Lead Engineer

james@supmenow.com <mailto:james@supmenow.com>⎥supmenow.com <http://supmenow.com/>
Sup

Runway East >

10 Finsbury Square

London

> EC2A 1AF

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #7

How do you introduce backfill extensions only for older systems? I just tested this with

extension String {
    @available(iOS, obsoleted=9.0)
    func localizedStandardContainsString(str: String) -> Bool {
        NSLog("calling %@", __FUNCTION__)
        return rangeOfString(str, options: [.CaseInsensitiveSearch, .DiacriticInsensitiveSearch], locale: NSLocale.currentLocale()) != nil
    }
}

but despite the `obsoleted=9.0`, my implementation here is in fact invoked from the code `"foobarbaz".localizedStandardContainsString("bar")` when running in the iOS 9.2 Simulator.

This behavior isn't really all that surprising, but since there's no way within that method to explicitly invoke the "real" method (since I've shadowed it so there's no way to refer to the framework-provided one), I can't even introduce my own #available() check within the method.

What you can do is introduce a different API that uses #available() to call the real API or fall back to a compatibility implementation, but I wouldn't call that a "backfill" since it's a different API.

-Kevin Ballard

···

On Tue, Feb 2, 2016, at 08:26 AM, Joe Groff via swift-evolution wrote:

Polyfills have their own tradeoffs; they tend to encourage constant accretion of glue code as new versions get added if there's no pressure to drop old versions, leading to a significant amount of the multi-megabyte Javascript framework downloads we all complain about these days. That said, you might be able to use the related `@available` attribute to introduce backfill extensions that are only available on older systems.

-Joe

On Feb 2, 2016, at 2:03 AM, James Campbell via swift-evolution <swift-evolution@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
*___________________________________*

*James⎥Lead Engineer*

*james@supmenow.com⎥supmenow.com*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_________________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Douglas Gregor) #8

Using function availability has proven fragile in the past too. A function may be present but private on older system, and have a slightly different behavior or crash, and so should not be used.

This is a failing of the -respondsToSelector: idiom for checking availability. Swift’s #available feature checks the actual OS version, so it doesn’t suffer from this problem.

  - Doug

···

On Feb 3, 2016, at 5:10 AM, Jean-Daniel Dupas via swift-evolution <swift-evolution@swift.org> wrote:

Le 2 févr. 2016 à 11:03, James Campbell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

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
___________________________________

James⎥Lead Engineer

james@supmenow.com <mailto:james@supmenow.com>⎥supmenow.com <http://supmenow.com/>
Sup

Runway East >>

10 Finsbury Square

London

>> EC2A 1AF

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joe Groff) #9

That sounds like a diagnostic bug. Got an example you can file in a bug report?

-Joe

···

On Feb 3, 2016, at 1:20 PM, Zach Waldowski via swift-evolution <swift-evolution@swift.org> wrote:

Joe —

Is this intended to be a supported behavior? I was all excited the other day to discover this behavior, using it to backport iOS 9's NSLayoutConstraint DSL. It mostly works, but as soon as an expression gets relatively complex (array literals >5 or so) or has any sort of error in it, Swift falls back to reporting an availability error.


(Félix Cloutier) #10

Won't it be a concern with a cross-platform Swift?

Félix

···

Le 3 févr. 2016 à 15:47:15, Douglas Gregor via swift-evolution <swift-evolution@swift.org> a écrit :

On Feb 3, 2016, at 5:10 AM, Jean-Daniel Dupas via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Using function availability has proven fragile in the past too. A function may be present but private on older system, and have a slightly different behavior or crash, and so should not be used.

This is a failing of the -respondsToSelector: idiom for checking availability. Swift’s #available feature checks the actual OS version, so it doesn’t suffer from this problem.

  - Doug

Le 2 févr. 2016 à 11:03, James Campbell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

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
___________________________________

James⎥Lead Engineer

james@supmenow.com <mailto:james@supmenow.com>⎥supmenow.com <http://supmenow.com/>
Sup

Runway East >>>

10 Finsbury Square

London

>>> EC2A 1AF

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Waite) #11

I’m not sure you could backfill extensions in all cases anyway, as those selectors may be on the particular objc objects as private API.

-DW

···

On Feb 4, 2016, at 5:55 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

How do you introduce backfill extensions only for older systems? I just tested this with

extension String {
   @available(iOS, obsoleted=9.0)
   func localizedStandardContainsString(str: String) -> Bool {
       NSLog("calling %@", __FUNCTION__)
       return rangeOfString(str, options: [.CaseInsensitiveSearch, .DiacriticInsensitiveSearch], locale: NSLocale.currentLocale()) != nil
   }
}

but despite the `obsoleted=9.0`, my implementation here is in fact invoked from the code `"foobarbaz".localizedStandardContainsString("bar")` when running in the iOS 9.2 Simulator.

This behavior isn't really all that surprising, but since there's no way within that method to explicitly invoke the "real" method (since I've shadowed it so there's no way to refer to the framework-provided one), I can't even introduce my own #available() check within the method.

What you can do is introduce a different API that uses #available() to call the real API or fall back to a compatibility implementation, but I wouldn't call that a "backfill" since it's a different API.

-Kevin Ballard

On Tue, Feb 2, 2016, at 08:26 AM, Joe Groff via swift-evolution wrote:

Polyfills have their own tradeoffs; they tend to encourage constant accretion of glue code as new versions get added if there's no pressure to drop old versions, leading to a significant amount of the multi-megabyte Javascript framework downloads we all complain about these days. That said, you might be able to use the related `@available` attribute to introduce backfill extensions that are only available on older systems.

-Joe

On Feb 2, 2016, at 2:03 AM, James Campbell via swift-evolution <swift-evolution@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
*___________________________________*

*James⎥Lead Engineer*

*james@supmenow.com⎥supmenow.com*

*Sup*

*Runway East

*

*10 Finsbury Square*

*London*

*

EC2A 1AF *

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_________________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(James Campbell) #12

Thanks for that detailed explanation :slight_smile:

For 1) I'm thinking with Polyfills if Swift were to have them we would
ideally need some sort of mixin system. I would love to see a way where we
could specify which polyfill to apply like this.

extension CLLocationManager {

include RequestLocationPolyFill

}

or maybe even:

include CoreLocation apply RequestLocationPolyFill //But not sure about this

Perhaps if two things tried to polyfill the same thing we would reject the
ability to include that polyfill (we sort of have this issue already when
two things try to define the same method via an extension.

I think for 2) as Chris explained above we drop feature detection and for
these Polyfills we would try to detect the version for a certain Module. As
he states once Swift PM has matured enough it ciuld provide this
information. So if I wanted to polyfill requestLocation from the
CoreLocation library I could do this:

@avaliable(CoreLocation, < 9) //Apply to CoreLocation version 9.0 or below
(Assuming CoreLocation is versioned same as iOS)
mixin RequestLocationPolyFill {
func requestLocation() {
}
}

···

*___________________________________*

*James⎥Lead Engineer*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On Thu, Feb 4, 2016 at 9:04 PM, Devin Coughlin <dcoughlin@apple.com> wrote:

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@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

*___________________________________*

*James⎥Lead Engineer*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com/>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Douglas Gregor) #13

Won't it be a concern with a cross-platform Swift?

#available is currently only implemented for Apple platforms. If someone wants to extend it to another platform, they need to do so in a manner that gives it consistent semantics.

  - Doug

···

On Feb 3, 2016, at 1:02 PM, Félix Cloutier <felixcca@yahoo.ca> wrote:

Félix

Le 3 févr. 2016 à 15:47:15, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

On Feb 3, 2016, at 5:10 AM, Jean-Daniel Dupas via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Using function availability has proven fragile in the past too. A function may be present but private on older system, and have a slightly different behavior or crash, and so should not be used.

This is a failing of the -respondsToSelector: idiom for checking availability. Swift’s #available feature checks the actual OS version, so it doesn’t suffer from this problem.

  - Doug

Le 2 févr. 2016 à 11:03, James Campbell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

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
___________________________________

James⎥Lead Engineer

james@supmenow.com <mailto:james@supmenow.com>⎥supmenow.com <http://supmenow.com/>
Sup

Runway East >>>>

10 Finsbury Square

London

>>>> EC2A 1AF

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Félix Cloutier) #14

But isn't it exactly across platforms that feature detection is the most important?

Félix

···

Le 3 févr. 2016 à 16:03:28, Douglas Gregor <dgregor@apple.com> a écrit :

On Feb 3, 2016, at 1:02 PM, Félix Cloutier <felixcca@yahoo.ca <mailto:felixcca@yahoo.ca>> wrote:

Won't it be a concern with a cross-platform Swift?

#available is currently only implemented for Apple platforms. If someone wants to extend it to another platform, they need to do so in a manner that gives it consistent semantics.

  - Doug

Félix

Le 3 févr. 2016 à 15:47:15, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

On Feb 3, 2016, at 5:10 AM, Jean-Daniel Dupas via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Using function availability has proven fragile in the past too. A function may be present but private on older system, and have a slightly different behavior or crash, and so should not be used.

This is a failing of the -respondsToSelector: idiom for checking availability. Swift’s #available feature checks the actual OS version, so it doesn’t suffer from this problem.

  - Doug

Le 2 févr. 2016 à 11:03, James Campbell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

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
___________________________________

James⎥Lead Engineer

james@supmenow.com <mailto:james@supmenow.com>⎥supmenow.com <http://supmenow.com/>
Sup

Runway East >>>>>

10 Finsbury Square

London

>>>>> EC2A 1AF

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(James Campbell) #15

I think if we did feature detection it should ignore private methods not
accessible by the code querying its accesbility. Additionally we really do
need proper support across platforms.

···

*___________________________________*

*James⎥Lead Engineer*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On Wed, Feb 3, 2016 at 9:03 PM, Douglas Gregor via swift-evolution < swift-evolution@swift.org> wrote:

On Feb 3, 2016, at 1:02 PM, Félix Cloutier <felixcca@yahoo.ca> wrote:

Won't it be a concern with a cross-platform Swift?

#available is currently only implemented for Apple platforms. If someone
wants to extend it to another platform, they need to do so in a manner that
gives it consistent semantics.

- Doug

Félix

Le 3 févr. 2016 à 15:47:15, Douglas Gregor via swift-evolution < > swift-evolution@swift.org> a écrit :

On Feb 3, 2016, at 5:10 AM, Jean-Daniel Dupas via swift-evolution < > swift-evolution@swift.org> wrote:

Using function availability has proven fragile in the past too. A function
may be present but private on older system, and have a slightly different
behavior or crash, and so should not be used.

This is a failing of the -respondsToSelector: idiom for checking
availability. Swift’s #available feature checks the actual OS version, so
it doesn’t suffer from this problem.

- Doug

Le 2 févr. 2016 à 11:03, James Campbell via swift-evolution < > swift-evolution@swift.org> a écrit :

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

*___________________________________*

*James⎥Lead Engineer*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com/>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jean-Daniel) #16

I think you can workaround the private selector issue by using the compiler knowledge of the availability version.

The compiler can mangle the polyfill symbol differently (even if it is a selector), and perform a version check at call site.

···

Le 5 févr. 2016 à 04:06, David Waite via swift-evolution <swift-evolution@swift.org> a écrit :

I’m not sure you could backfill extensions in all cases anyway, as those selectors may be on the particular objc objects as private API.

-DW

On Feb 4, 2016, at 5:55 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

How do you introduce backfill extensions only for older systems? I just tested this with

extension String {
  @available(iOS, obsoleted=9.0)
  func localizedStandardContainsString(str: String) -> Bool {
      NSLog("calling %@", __FUNCTION__)
      return rangeOfString(str, options: [.CaseInsensitiveSearch, .DiacriticInsensitiveSearch], locale: NSLocale.currentLocale()) != nil
  }
}

but despite the `obsoleted=9.0`, my implementation here is in fact invoked from the code `"foobarbaz".localizedStandardContainsString("bar")` when running in the iOS 9.2 Simulator.

This behavior isn't really all that surprising, but since there's no way within that method to explicitly invoke the "real" method (since I've shadowed it so there's no way to refer to the framework-provided one), I can't even introduce my own #available() check within the method.

What you can do is introduce a different API that uses #available() to call the real API or fall back to a compatibility implementation, but I wouldn't call that a "backfill" since it's a different API.

-Kevin Ballard

On Tue, Feb 2, 2016, at 08:26 AM, Joe Groff via swift-evolution wrote:

Polyfills have their own tradeoffs; they tend to encourage constant accretion of glue code as new versions get added if there's no pressure to drop old versions, leading to a significant amount of the multi-megabyte Javascript framework downloads we all complain about these days. That said, you might be able to use the related `@available` attribute to introduce backfill extensions that are only available on older systems.

-Joe

On Feb 2, 2016, at 2:03 AM, James Campbell via swift-evolution <swift-evolution@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
*___________________________________*

*James⎥Lead Engineer*

*james@supmenow.com⎥supmenow.com*

*Sup*

*Runway East

*

*10 Finsbury Square*

*London*

*

EC2A 1AF *

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_________________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Charles Srstka) #17

How is that going to work, though? Most of the system APIs are in Objective-C, which has no distinction between private and public methods at runtime.

I think #available is fine as-is, especially since the compiler is able to detect if you’re using an API that’s not appropriate for the OS X version you’re specifying.

Charles

···

On Feb 3, 2016, at 3:07 PM, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

I think if we did feature detection it should ignore private methods not accessible by the code querying its accesbility. Additionally we really do need proper support across platforms.


(James Campbell) #18

So if we treat an operating and apps as a collection of libraries (I.e modules) which can be vended to various other swift modules.
How would we handle library version querying ? Is there support for this already? You are right in my polyfill library I query the version of the core foundation library (mainly due to the lack of the ability to do so with UIKit)
If we were able to query libraries (I.e modules) it would be great.
- We could query the version of the standard library of swift which you could use to know the version of swift itself- Features across platforms and versions of platforms could be queued simply by knowing if a certain module was loaded (from a library) and what version so you can handle older Apis.
Functionality that may not be implemented across platforms should be split into its own library or modules to simplify querying if that feature exists on this platform.
If you have a class shared across platforms but one platform contains platform specific extensions to that class. Then the class would be in the shared module and you could put the extension in a separate module (I.e MyModuleAppleWatchExtensions).
You should be able to version the methods by the version of the module but only the modules by the specifications of the platform.
This way we can handle multiple platforms, be less fragile and not encourage people effectively whitelisting a bunch of operating systems in their APIs.
I think this would work reall nice with the swift PM

P.s I wanted to check do we have specifies for watch and tvos?

···

Sent from Outlook Mobile

On Wed, Feb 3, 2016 at 5:27 PM -0800, "Charles Srstka" <cocoadev@charlessoft.com> wrote:

On Feb 3, 2016, at 3:29 PM, James Campbell <james@supmenow.com> wrote:

We could only allow objective c methods to be queried by responds to
But then the check is faulty, because it can find private methods.
But how do we handle Linux or any of the other platforms are you saying we need to annotate with a million OS version specifies

If you’re checking to see whether a library contains an API or not, you probably only care about the library version rather than the OS itself.
Charles
Sent from Outlook Mobile

On Wed, Feb 3, 2016 at 1:18 PM -0800, "Charles Srstka" <cocoadev@charlessoft.com> wrote:

On Feb 3, 2016, at 3:07 PM, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

I think if we did feature detection it should ignore private methods not accessible by the code querying its accesbility. Additionally we really do need proper support across platforms.
How is that going to work, though? Most of the system APIs are in Objective-C, which has no distinction between private and public methods at runtime.
I think #available is fine as-is, especially since the compiler is able to detect if you’re using an API that’s not appropriate for the OS X version you’re specifying.
Charles


(Chris Lattner) #19

We have a vague desire (vague because it hasn’t been designed and built out) for #available to be extended to arbitrary modules. SwiftPM is using semantic versioning for its packages, and that is exactly what we’d want to tie into an availability mechanism. When SwiftPM is more mature, we can explore building out the language model for this.

-Chris

···

On Feb 4, 2016, at 12:56 AM, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

So if we treat an operating and apps as a collection of libraries (I.e modules) which can be vended to various other swift modules.

How would we handle library version querying ?