Conditional compilation for Attributes in Swift 5.8
Table of Contents
Why do we need Conditional compilation for attributes
As Swift evolved, it introduced many new attributes to the language, e.g., @preconcurrency
introduced in SE-0337.
However, adopting these new attributes means the source code won't compile with an older compiler that doesn't recognize the new attributes.
We can use conditional compilation to compile code based on the compiler version.
#if compiler(>=5.6)
@preconcurrency
protocol P: Sendable {
func f()
func g()
}
#else
protocol P: Sendable {
func f()
func g()
}
#endif
There are two problems with this approach.
-
Code duplication since we have to repeat the code multiple times.
-
Checking for the compiler version is error-prone.
Unlike an availability condition, e.g.,if #available(iOS 15, *)
. Swift attributes don't tie to specific compiler versions.The availability condition works because all API properties and classes are marked with the version it supports via an
@available
attribute.@available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *)
public func fontWeight(_ weight: Font.Weight?) -> some ViewBut the compiler version that Swift attribute was introduced *isn't documented anywhere.
To make the situation worse, the availability of some attributes can depend on platform and configuration flags: for example,
@objc
is only available when the Swift runtime has been compiled for interoperability with Objective-C.
To make the process of introducing new attributes less troublesome in the future, Swift 5.8 introduced Conditional compilation for attributes.
You can easily support sarunw.com by checking out this sponsor.
Offline Transcription: Fast, privacy-focus way to transcribe audio, video, and podcast files. No data leaves your Mac.
Conditional compilation for attributes
Swift 5.8 add a conditional directive hasAttribute(AttributeName)
that evaluates true when the compiler has support for the attribute with the specified name, AttributeName
.
We can use it like another conditional directive.
#if compiler(>=5)
print("Compiled with the Swift 5 compiler or later")
#endif
#if swift(>=4.2)
print("Compiled in Swift 4.2 mode or later")
#endif
#if hasAttribute(preconcurrency)
@preconcurrency
#endif
As a result, we can make the code less repetitive and more concise.
#if hasAttribute(preconcurrency)
@preconcurrency
#endif
protocol P: Sendable {
func f()
func g()
}
You can easily support sarunw.com by checking out this sponsor.
Offline Transcription: Fast, privacy-focus way to transcribe audio, video, and podcast files. No data leaves your Mac.
Summary
This change might not be beneficial for an API consumer because you control the code and compiler, so you can make sure it matches.
But this will greatly benefit the framework author because the source code can be used in many circumstances.
Read more article about Swift, Swift 5.8, or see all available topic
Enjoy the read?
If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.
Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.
If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.
Become a patron Buy me a coffee Tweet ShareHow to initialize @Binding in SwiftUI
Learn how to implement a custom init that accepts a @Binding variable.
How to open URL in Safari in SwiftUI
In SwiftUI, there are two ways to open a URL in Safari, Link view, and openURL environment value.