An Early Look at Swift Macros – WWDC 2023

Here’s an early look at Swift Macros, a feature introduced in WWDC 2023 to allow developers to eliminate boilerplate code and generally make some things easier.

Continue Reading…

Written by

J.C. Yee

Published on

13 Jun 2023

Written by Iñaki Narciso

Writing code can sometimes introduce a lot of ceremony (and repetition). This repetitive code is what we call a boilerplate code, and it lurks on a lot of projects out there!

Every year, Swift gets new language features that aims to gradually remove a lot of boilerplate code, all thanks to the developers active in the Swift community!

In this year’s WWDC, the Swift team introduced Swift Macros – a new way to allow developers to add language features that would eliminate boilerplate and generally make tedious things easier.

What are Swift Macros?

Before Swift Macros, developers need to make proposals to the Swift team for improvements, which involves a lot of discussion and conference calls. Once a proposal is accepted, they include it as a language feature update to the Swift compiler which is then released as part of the next Xcode update.

Using Swift Macros, a developer can add a new language feature, without going through the entire process of filing a proposal, and be able to distribute it as a Swift package via SPM (Swift Package Manager).

Creating a Swift Macro

Swift Macros can be created as a Swift package in Xcode 15 (currently in Beta). To create a Macro, open Xcode 15. In Xcode’s menu bar, open File > New > Package.

Select Swift Macro from the list of project templates for a Swift package:

Provide a name for your Swift Macro package, then click on Create to create the project. For this example, we’ll name it WWDC:

A Swift package will be created containing an example of the #stringify Swift Macro as demonstrated in WWDC23 session: Write Swift Macros.

To see what the #stringify Macro does, open up main.swift from WWDCClient group:

Here, we can see that #stringify accepts a Swift expression a + b which is an addition of two Int numbers, and returns a Tuple as the result.

Swift Macros work similar to a function wherein it accepts argument parameters. In reality, Macros use functions under the hood. You can open WWDCMacro.swift if you want to take a peek on how the Macro provides an expansion functionality to the code. Let’s see what the #stringify looks like when expanded.

To check the code generated by the Macro, you can right click on the Macro to reveal the menu, and select Expand Macro.

Xcode should be able to show the code that’s generated when this Macro is used:

Why Use Macros?

Macros are generally a way to extend Swift’s language features, allowing developers to find a way to reduce tedium and avoid repetitive boilerplate code.

To understand what a Macro does, let’s take a look at a language feature that does the same thing as a Macro does: derived conformances of protocols.

For example, when you have a model that conforms to the Codable protocol without providing implementations to its members:

struct StarSystem: Codable {
    var name, description: String
    var galaxy: Galaxy
    var planets: [Planet]

Swift automatically expands this conformance to provide Encodable and Decodable implementations to its members, so basically inserting the code below without even the need to write it:

struct StarSystem: Codable {
    var name, description: String
    var galaxy: Galaxy
    var planets: [Planet]

    /// Inserted by Swift with derived conformances
    private enum CodingKeys: String, CodingKey {
        case name, description, galaxy, planets
    init(from decoder: Decoder) throws { ... }
    func encode(to encoder: Encoder) throws { ... }

Swift creating this code for you lets you use Codable without having to write a screenful of code. Macros work in the same way.

Macros expand hidden implementations so you can focus on the intent of writing code instead of being hindered by the ceremony of repeating boilerplate code.

Swift Macros

There are two kinds of Macros in Swift: #freestanding and @Attached macros.

Free standing macros takes the place of an expression or declaration, and it always start with a # sign.

return #stringify(a + b)

Attached macros are used as attributes on declarations in your code, and it always start with an @ sign.

func sendRequest() async throws -> Response

WWDC’23 has a lot of new exciting stuff for Swift and SwiftUI, and Swift Macros empowers some of the new things like the new DataFlow for SwiftUI, as well as Swift Data.

We will cover more in depth on how to create a Swift Macro in another article!