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.
Rod_Brown
(Rod Brown)
2
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
Rod_Brown
(Rod Brown)
4
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.
felix91gr
(Félix Fischer)
6
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 
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
Rod_Brown
(Rod Brown)
9
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...