Conditional compilation for Attributes in Swift 5.8

⋅ 2 min read ⋅ Swift 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.

  1. Code duplication since we have to repeat the code multiple times.

  2. 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 View

    But 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.

Sponsor sarunw.com and reach thousands of iOS developers.

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.

Sponsor sarunw.com and reach thousands of iOS developers.

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 Share
Previous
How to initialize @Binding in SwiftUI

Learn how to implement a custom init that accepts a @Binding variable.

Next
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.

← Home