deployment targets and frameworks

Suppose *Apple* ships a framework that is only supported in iOS 9.3. As a direct consequence, the framework is only #available in iOS 9.3 or later.

Suppose Jane links this framework into her iOS application. The deployment target for her application *can be any value*. She sets the framework to be weakly linked, and as long as the code that uses the Apple framework is guarded by a 9.3 availability check, she can deploy back to 8.0, 7.0, etc.

Suppose *I* ship a custom framework that I only want to bother supporting for iOS 9.3 users. I'm not testing on old OS, I don't have CI on old OS, and quite frankly I have no idea if it works. And I'm not in the habit of shipping code that wasn't even tested on my machine. As a direct consequence, I set my framework deployment target to 9.3.

Now Jane links this framework into her "deployment target 8.0" application. She weakly links it and uses availability checks just like she did with the Apple framework. But this time the compiler says no:

error: module file's minimum deployment target is ios9.3 v9.3

Jane now has a set of bad choices:

1. She can not use my framework at all
2. She can drop support for <9.3 entirely from her application in order to use my framework
3. She can convince me to support iOS 8, when I don't want to invest in the QA and test time.
4. She can convince me to set my deployment target to 8, try to find all my public APIs, sprinkle `@available(iOS 9.3, *)` everywhere and hope I didn't miss any. Spoiler alert: that's what I did all afternoon.

This is too hard. IMO Jane should be able to use my "9.3+" frameworks as easily as she can use Apple's.

IMO, when Jane imports a "DT 9.3" framework, into her "DT 8.0" application, it should A) import successfully, B) link weakly, and C) have `@availability(9.3, *)` overlayed on the interface.

There may be some subtle additional details because I don't know exactly the implementation of these features, but that's the general idea.

+1 to this. It's really important for getting a sane third-party library
ecosystem working.

I assume that the @available annotation generated would also work for
watchOS, tvOS, etc. frameworks. How would it work for non-iOS Swift
platforms? I'm not terribly familiar with how #available works on Linux,
but the versioning problem exists there too.

···

On Mon, Apr 4, 2016 at 4:48 PM, Drew Crawford via swift-evolution < swift-evolution@swift.org> wrote:

Suppose **Apple** ships a framework that is only supported in iOS 9.3.
As a direct consequence, the framework is only #available in iOS 9.3 or
later.

Suppose Jane links this framework into her iOS application. The
deployment target for her application *can be any value*. She sets the
framework to be weakly linked, and as long as the code that uses the Apple
framework is guarded by a 9.3 availability check, she can deploy back to
8.0, 7.0, etc.

Suppose *I* ship a custom framework that I only want to bother supporting
for iOS 9.3 users. I'm not testing on old OS, I don't have CI on old OS,
and quite frankly I have no idea if it works. And I'm not in the habit of
shipping code that wasn't even tested on my machine. As a direct
consequence, I set my framework deployment target to 9.3.

Now Jane links this framework into her "deployment target 8.0"
application. She weakly links it and uses availability checks just like
she did with the Apple framework. But this time the compiler says no:

error: module file's minimum deployment target is ios9.3 v9.3

Jane now has a set of bad choices:

1. She can not use my framework at all
2. She can drop support for <9.3 entirely from her application in order
to use my framework
3. She can convince me to support iOS 8, when I don't want to invest in
the QA and test time.
4. She can convince me to set my deployment target to 8, try to find all
my public APIs, sprinkle `@available(iOS 9.3, *)` everywhere and hope I
didn't miss any. Spoiler alert: that's what I did all afternoon.

This is too hard. IMO Jane should be able to use my "9.3+" frameworks as
easily as she can use Apple's.

IMO, when Jane imports a "DT 9.3" framework, into her "DT 8.0"
application, it should A) import successfully, B) link weakly, and C) have
`@availability(9.3, *)` overlayed on the interface.

There may be some subtle additional details because I don't know exactly
the implementation of these features, but that's the general idea.

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

I assume that the @available annotation generated would also work for watchOS, tvOS, etc. frameworks.

Yes

How would it work for non-iOS Swift platforms? I'm not terribly familiar with how #available works on Linux, but the versioning problem exists there too.

In my experience, "linux" cannot be listed inside @available. I think we need to add it.

I'm not sure the "version" of Linux is meaningful (e.g. - interpreted as kernel version? glibc?) but I think clearly "linux" the vaguely-versioned platform should be allowed in an @availability attribute.

···

On Apr 4, 2016, at 7:01 PM, Jonathan Tang <jonathan.d.tang@gmail.com> wrote:

I would not want this to be implicit behavior: it should be recorded in the source with, e.g.,

  @availability(iOS: 9.3) import YourCustomFramework

so that it is clear that the imported declarations are only available on iOS 9.3 or newer.

  - Doug

···

On Apr 4, 2016, at 4:48 PM, Drew Crawford via swift-evolution <swift-evolution@swift.org> wrote:

Suppose *Apple* ships a framework that is only supported in iOS 9.3. As a direct consequence, the framework is only #available in iOS 9.3 or later.

Suppose Jane links this framework into her iOS application. The deployment target for her application *can be any value*. She sets the framework to be weakly linked, and as long as the code that uses the Apple framework is guarded by a 9.3 availability check, she can deploy back to 8.0, 7.0, etc.

Suppose *I* ship a custom framework that I only want to bother supporting for iOS 9.3 users. I'm not testing on old OS, I don't have CI on old OS, and quite frankly I have no idea if it works. And I'm not in the habit of shipping code that wasn't even tested on my machine. As a direct consequence, I set my framework deployment target to 9.3.

Now Jane links this framework into her "deployment target 8.0" application. She weakly links it and uses availability checks just like she did with the Apple framework. But this time the compiler says no:

error: module file's minimum deployment target is ios9.3 v9.3

Jane now has a set of bad choices:

1. She can not use my framework at all
2. She can drop support for <9.3 entirely from her application in order to use my framework
3. She can convince me to support iOS 8, when I don't want to invest in the QA and test time.
4. She can convince me to set my deployment target to 8, try to find all my public APIs, sprinkle `@available(iOS 9.3, *)` everywhere and hope I didn't miss any. Spoiler alert: that's what I did all afternoon.

This is too hard. IMO Jane should be able to use my "9.3+" frameworks as easily as she can use Apple's.

IMO, when Jane imports a "DT 9.3" framework, into her "DT 8.0" application, it should A) import successfully, B) link weakly, and C) have `@availability(9.3, *)` overlayed on the interface.

Strong +1 on this. Been there myself.

A.

Would you promote using this syntax for the Apple frameworks as well?

A major goal for me is syntax consistency between Apple's and third-party frameworks. That way the knowledge of how to use one transfers to the other, and we ensure people with fresh ideas about how to build frameworks are not burdened with educating application developers about "novel" import syntax.

From consulting the table <Featured | Apple Developer Documentation, developers with a DT of 7.0 (which is the oldest Swift supports) would need to add e.g. @availability(iOS 8.0, *) import CloudKit for a few frameworks, but that seems like a pretty easy change.

···

On Apr 5, 2016, at 12:06 PM, Douglas Gregor <dgregor@apple.com> wrote:

I would not want this to be implicit behavior: it should be recorded in the source with, e.g.,

  @availability(iOS: 9.3) import YourCustomFramework

so that it is clear that the imported declarations are only available on iOS 9.3 or newer.

  - Doug

Apple frameworks tend to have ail their various classes and other APIs annotated with availability attributes, so I wouldn’t expect to need this import syntax for any of those. Really, this syntax is a shorthand for “treat the imported library as if the author had put this availability annotation on all of its public APIs”. If your goal is consistency between Apple frameworks and other frameworks, I don’t think this is the way to go.

  - Doug

···

On Apr 5, 2016, at 4:04 PM, Drew Crawford <drew@sealedabstract.com> wrote:

On Apr 5, 2016, at 12:06 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

I would not want this to be implicit behavior: it should be recorded in the source with, e.g.,

  @availability(iOS: 9.3) import YourCustomFramework

so that it is clear that the imported declarations are only available on iOS 9.3 or newer.

  - Doug

Would you promote using this syntax for the Apple frameworks as well?

A major goal for me is syntax consistency between Apple's and third-party frameworks. That way the knowledge of how to use one transfers to the other, and we ensure people with fresh ideas about how to build frameworks are not burdened with educating application developers about "novel" import syntax.

Does this mean that the recommended best practice for framework authors is
to set the deployment target low and then add an availability attribute for
every public symbol?

···

On Wed, Apr 6, 2016 at 9:58 AM, Douglas Gregor via swift-evolution < swift-evolution@swift.org> wrote:

On Apr 5, 2016, at 4:04 PM, Drew Crawford <drew@sealedabstract.com> wrote:

On Apr 5, 2016, at 12:06 PM, Douglas Gregor <dgregor@apple.com> wrote:

I would not want this to be implicit behavior: it should be recorded in
the source with, e.g.,

@availability(iOS: 9.3) import YourCustomFramework

so that it is clear that the imported declarations are only available on
iOS 9.3 or newer.

- Doug

Would you promote using this syntax for the Apple frameworks as well?

A major goal for me is syntax consistency between Apple's and third-party
frameworks. That way the knowledge of how to use one transfers to the
other, and we ensure people with fresh ideas about how to build frameworks
are not burdened with educating application developers about "novel" import
syntax.

Apple frameworks tend to have ail their various classes and other APIs
annotated with availability attributes, so I wouldn’t expect to need this
import syntax for any of those. Really, this syntax is a shorthand for
“treat the imported library as if the author had put this availability
annotation on all of its public APIs”. If your goal is consistency between
Apple frameworks and other frameworks, I don’t think this is the way to go.

Every public symbol that needs it, sure. Fortunately, Swift tells you when you get it wrong.

  - Doug

···

On Apr 6, 2016, at 11:04 AM, Jonathan Tang <jonathan.d.tang@gmail.com> wrote:

On Wed, Apr 6, 2016 at 9:58 AM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Apr 5, 2016, at 4:04 PM, Drew Crawford <drew@sealedabstract.com <mailto:drew@sealedabstract.com>> wrote:

On Apr 5, 2016, at 12:06 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

I would not want this to be implicit behavior: it should be recorded in the source with, e.g.,

  @availability(iOS: 9.3) import YourCustomFramework

so that it is clear that the imported declarations are only available on iOS 9.3 or newer.

  - Doug

Would you promote using this syntax for the Apple frameworks as well?

A major goal for me is syntax consistency between Apple's and third-party frameworks. That way the knowledge of how to use one transfers to the other, and we ensure people with fresh ideas about how to build frameworks are not burdened with educating application developers about "novel" import syntax.

Apple frameworks tend to have ail their various classes and other APIs annotated with availability attributes, so I wouldn’t expect to need this import syntax for any of those. Really, this syntax is a shorthand for “treat the imported library as if the author had put this availability annotation on all of its public APIs”. If your goal is consistency between Apple frameworks and other frameworks, I don’t think this is the way to go.

Does this mean that the recommended best practice for framework authors is to set the deployment target low and then add an availability attribute for every public symbol?

Fortunately, Swift tells you when you get it wrong.

It doesn't, though. That's the thing.

Consider CloudKit, first available in iOS 8. Does that framework's implementation rely on new, novel APIs only available on iOS 8, and the Swift compiler threw availability errors to the implementor of CloudKit? Or, is the reason the reason it's "unavailable" in 7 because Apple decided as a point of business not to compile, test, and QA an iOS 7.3 that added CloudKit? Well I don't have the sourcecode so I don't know, maybe there is some incredibly novel API involved in talking to computers on the internet not available in iOS 7. But I suspect it's mostly a business decision, not technical.

So, if we assume that's the case (or, if I'm wrong, replace CK with a framework for which there is no technical reason why it's unavailable), then Apple paid some QA engineer to sprinkle @available on all the public APIs. Meanwhile, third-party developers in this situation are similarly making business decisions not to support iOS 7/8, and we also have to grab our QA engineer and comb through the API very carefully.

I'm suggesting is we can all fire the QA engineer with a compiler feature.

Really, this syntax is a shorthand for “treat the imported library as if the author had put this availability annotation on all of its public APIs”.

What if we solve the problem at "framework-compile-time" rather than at "import-time"?

Suppose we have some driver flag that injects @availability onto every decl in the module. For decls with existing @availability it simply raises the floor to some minimum value. Now we distribute the module, and the availabilities have been set.

My intuition would be to use the existing "deployment target" flag to control this feature, since this is how framework authors actually do express their intent to support an OS. But if we need to introduce a novel concept of an "availability target", that's fine too.

The important thing is that it's zero-effort for consumers of the framework, and I get to fire my QA engineer.

···

On Apr 6, 2016, at 1:35 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote: