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.
@AddCompletionHandler 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!