Public API condition depending on swift version of user


(Alfred Zien) #1

During transition from Swift 4.1 to Swift 4.2 we faced an issue. Suppose we have 3 targets: Utils, App1 and App2.
In Utils we have different util functions, including ones that gives ability to build in both versions of Swift during transition, for example:

import SDK

#if swift(>=4.1.50)
#else
extension SDK.Foo {
  public func foo(...) { ... } // Method available only in swift 4.2
}
#endif

If we want to switch one of those targets to Swift 4.2 it will not compile. We can't switch Utils to 4.2 first, as method foo will not be out there and that will force App1 and App2 use 4.2. We cannot switch App1 or App2 to 4.2 either, as it will see foo both from Utils and SDK. So we forced to migrate all code at once, or copypaste that code from utils to each target that it uses, or extract such code to another target, like UtilsCompatability4_1_50 and import it to targets that are not migrated yet.

Neither of those seems like good thing, ruining idea about gradually moving to new swift from compatability mode. So, is there any plans to conditionally provide parts of public API? So, for example, symbol foo in Utils will be visible from App1 only if App1 compiles with certain version of Swift, much like in C headers, where conditional preprocessor directives in public headers are resolved relative to "users" macro definitions.


(Jordan Rose) #2

I think your best option is to contain the damage in Utils by renaming the function:

import SDK

extension SDK.Foo {
#if swift(>=4.1.50)
  public func myFoo(...) { self.foo(...) }
#else
  public func myFoo(...) { ... } // Method available only in swift 4.2
#endif
}

In general the kind of "polyfill" technique you're describing isn't well-supported in Swift, but it's questionable whether we want to encourage that pattern (based on static checks or dynamic checks). Here's an old discussion about some of the pros and cons, though in a dynamic-checking context.