Is there a plan to allow conditional escalation of SDK at compile time?

Hi,

I have an open source Swift framework (https://flint.tools) that I want to use to wrap new Siri Shortcuts APIs. However I don't want to force all the users of my framework to build with Xcode 10 (now or in future).

I don't believe I can achieve this with the current compile and runtime availability checks in Swift.

Here's an example:

#if os(iOS) || os(watchOS) 
   if #available(iOS 12, watchOS 5, *) {
      // Here's the new API
      myActivity.isEligibleForPrediction = true
   }
#endif

I can build this in Xcode 10, it works as expected at runtime. However I can't compile on Xcode 9.x because isEligibleForPrediction is not a valid symbol on Xcode 9.x contemporary SDKs.

Because this is a compile time constraint, it doesn't seem possible to tell Swift "Only compile this code if the target SDK is iOS >= 12 or watchOS >= 5" so that users of my framework who are not able to build on the latest and greatest can still use it.

Maintaining multiple branches over time is very painful.

Hi Marc,

I believe this can currently be accomplished by a workaround with a Swift version check:

#if swift(>=4.2) && (os(iOS) || os(watchOS))

   if #available(iOS 12, watchOS 5, *) {
      // Here's the new API
      myActivity.isEligibleForPrediction = true
   }

#endif

This would ensure that the availability check is only visible to the compiler when in Swift 4.2 and later, which is included in Xcode 10, where the symbol is first available.

That said, this is a workaround to your issue rather than a feature to handle these cases for Target SDK and I think it’s something that definitely needs addressing.

Also please note that this workaround doesn’t work if it is in Swift 3 compatibility mode. You can construct a more convoluted check around this, but a proposal documenting it and a more improved version was recently approved and should end up in the final Swift 4.2 I believe.

Thanks for that Rod, it's a less ugly workaround I think than e.g. #if canImport(Network).

It seems to me we need something like this:

#if sdk(iOS >= 12) || sdk(watchOS >= 5) 
...
#endif

Does that seem like a reasonable proposal?

1 Like

Personally I’d be in favour of this, yes. I think it’s clean at first glance. Would love to hear what others think of this...

Or maybe could use the os directives for consistency... though this could get confusing for whether you are compiling for that SDK or just running on a device running that version of the os...:

#if os(iOS >= 12) || os(watchOS >= 5) 
...
#endif
1 Like

Actually this is what os() currently does I think, it just doesn't have the SDK version so your proposal seems better to me.

I think you can evaluate the avaliability as a compile-time boolean with this pitch.

Then you could have it like this:

#if goodSDK()

Which would evaluate at compile time and help you conditionally compile it or not :slight_smile:

2 Likes

Hmm @Rod_Brown sadly the swift 4.2 option isn't really viable. There seems to be no way to make projects use the latest available Swift version, and conversion to Swift 4.2 involves other changes.

I think #if canImport(Network) is the only "safe" way and it is very ugly.

This is what I had to do in the end, it makes me sad:

#if canImport(Network) && (os(iOS) || os(watchOS))
        if #available(iOS 12, watchOS 5, *) {
            activity.isEligibleForPrediction = activityTypes.contains(.prediction)
        }
#endif
1 Like

Makes sense. I think the fact we have to check for the import ability of an irrelevant framework reveals this is an area that needs work. Hopefully the pitch @felix91gr mentioned can address this issue...