Swift Macro impression
On the latest Worldwide Developer Conference (WWDC 2023), Apple has introduced a new way on how to extend your source code using Swift Macro. It acts like a custom plugin to the Swift compiler that allows you to introduce additional checks or code generation. Code modification and deletion is not allowed. Nevertheless it will be a useful way to reduce repetitive tasks or easily solve tasks that were hard or impossible to achieve previously.
Here I would like to provide my personal impressions from my recent first Swift Macro experience. So brave yourself for plenty of subjective opinions.
Issues
The main limitation will be requirement of Xcode 15 version so make sure it already runs on your CI/CD machine. Other than that Macro will work on projects targeting most of older OS versions too, the minimum supported versions come from the dependency on SwiftSyntaxMacros
package, see Package.swift definition.
Since Swift Macro is relatively new to the Apple development, you can’t be surprised when there are some limitations or issues. One of the most noticeable issues is the build compilation time significant increase. I won’t get into much details as there is entire forum thread on this that will for sure bring more clarity. And from my experience, Apple sillicon does not resolve the issue as you might expect. While increased build time may not be a blocker, it is something to keep in mind because every minute counts today, especially if it costs you money! It can also negatively impact development time if you do frequent changes to the Macro package definition and perform clean build repeatedly.
Another issue I have encountered was that Xcode build froze to me from time to time. Unfortunately I did not find exact cause to this to know if the issue may still be present in these days. It was most probably a mistake on my end, however it would be nice to have at least some error message, right?
There’s also an inconvenient limitation that debugging with breakpoints is not possible using Run action as we all are used to. The only way seems to be using unit tests. So you will either have to go with Test Driven Development approach or your only other option probably are diagnostic logs which may not be as ideal and definitely more time consuming.
As you will find out, there are two targets required for your macro - one for the declaration and the other one for the actual implementation. You will get these for free upon new macro package creation. However, things in Swift Package Manager can get complicated when you want to share some codebase across multiple targets. This probably does not directly relates to Swift Macro as is but you should have good knowledge of SPM manifest file because Xcode warnings will not help you much on errors (most of the time I was facing some linking errors).
Resources
The one thing that surprised me though was the documentation. It’s not that there is too few of it. On contrary you’ll find plenty of articles, manuals and even examples out there. It’s just that the existing documentation is not as detailed as one would need or expect.
Of cource there is the official documentation from Apple but I was personaly missing some examples to each Macro type to catch the key differences easier. For example it took me some time to figure out what exact Macro kind will be the best fit for my case.
When the official documentation does not suffice or you don’t preferr written text, one can check several sessions on the subject from WWDC 2023, for example Write Swift macros and other related videos. Unfortunately they still not contain the level of details one may need.
What I found quite useful was this list of public Swift Macro implementations. You can either see if the Macro is already implemented by someone else so you could save your time and easily reuse. If not, it can help to check reference implementation and maybe even to gain some inspiration from there. This helped me a lot to be honest.
Last but not least, I would love to mention online Swift Abstract Syntax Tree (AST) explorer. This site is really a life saver as there’s definitely not enough documentation on this subject. Using the online tool is definitely much faster than manually exploring it on the go only by using the logs. Luckily you don’t have to instantiate entire AST for your return values as it can also be converted from raw String.
Usage
On the other hand Swift Macro may address many issues that we just got used to over the time. The biggest benefit probably is possibility to eliminate boilerplate code that can be generated automatically and to let you focus on implementation that really matters. It can be for example public init synthetizer on structs that is otherwise available only with internal access modifier. This previously required us to explicitly provide and maintain the code.
And there are many more publicly available Swift Macro implementations that might come really handy for you. For example I’ve seen some related to the Codable protocol. Another possible use may be in unit tests to automatically generate tests only by using different set of input values. Feel free to do your own research based on your actual needs.
In my case I was trying to implement a custom annotation on Combine Subject property that would automatically add more code in order to implement “lazy load” mechanism with option to handle retries and propagate potential errors to the consumer side.
Summary
Just to remind you, all the above are only my personal impressions after the recent first take on custom Swift Macro implementation. As I have mentioned several issues or limitations, there are also plenty of benefits. We already have two macros implemented in my current project and they do work as expected.
Keep in mind that the feature is still relatively new so we may see many improvements or changes over the next few versions. Any issue or information mentioned here may not apply for your project and you should evaluate all the benefits and risks specifically for your needs as with any new feature or technology.