WWDC 2023: Exploring new Swift Macros API

Sergey Petrachkov
Dev Genius
Published in
4 min readJun 9, 2023

--

It is the year 2023 and Apple have just introduced a new API to build macros for Swift in Swift. In the next few minutes, we’re gonna take a look at it and build our own macros.

Prerequisites:

  • Xcode 15 Beta or higher (to have the template in the IDE and dev tools in the system)
  • Swift 5.9
  • Free time

Let’s go!

First we need to open Xcode 15 and do

File → New → Package → Swift Macro

It’ll generate a package with a pre-defined structure and ready-made macros.

Is that all? Not really, let’s build our own macros. I usually like to explore new APIs trying to solve problems that I face in my daily work. For the last few years I’ve been working on really big projects and all of them were modularised to some extent.

One of the pain points has always been creating a memberwise initializer and exposing it to external consumers. Recent Xcode releases have the functionality to generate those, which is cool, but with the Macros API we can really boost our workflows.

Let’s create our PublicMemberwiseInitMacros 💪

Your package structure should look like this:

All the logic will reside in PublicMemberwiseInitMacroMacros target.

This macros will be registered in PublicMemeberwiseInitMacro.swift

It’s gonna be an attached member macro. For more info about different types see this: https://developer.apple.com/videos/play/wwdc2023/10166/

For the logic implementation, we’ll create `PublicMemberwiseInitMacro` struct under `PublicMemberwiseInitMacroMacros` target and create an `expansion` func.

What do we need to accomplish the task? Well, we need to go through all members of a struct or a class, filter out only stored properties, and create an `init` with all of those properties as arguments that will be assigned in the body of the initializer.

To get all stored properties we need to go through the syntax tree. You can debug it and see the hierarchy in Xcode console. So, basically, the stored property is a property where there’s only one binding that has either no accessors or the accessors are only property observers (willSet or didSet).

So, let’s make a simple extension to VariableDeclSyntax.

Then we can access `memberBlock.members` of a `DeclGroupSyntax` and take only variables that are stored properties.

Then the whole implementation will look like this:

  1. Get the stored properties
  2. Extract names and types from them
  3. Create ExprSyntax from String that would describe our typical initializer’s header and body
  4. Combine header and body with `InitializerDeclSyntax`

How to test it?

The project contains a test target and macros helpers. So, it should be possible to do something like this:

How to consume our macros? Let’s go to the `PublicMemeberwiseInitMacroClient` and `main.swift`. Import our macros and create a few classes or structs. Add the annotation on top of the declaration and see this:

That’s pretty much it!

For the theory, please see Apple docs. The source codes of this macros are available in this repo and are free to use and play with https://github.com/SergeyPetrachkov/PublicMemberwiseInitMacro

--

--

Amsterdam-based iOS developer born and bred in Siberia. Follow my "Metal in the Attic" Youtube channel :)