A Possible Vision for Macros in Swift

It has neither been overlooked nor is it necessarily possible :slight_smile:

The attached macros proposal provides more detail here, but we are not proposing to allow the kinds of completely arbitrary syntax rewriting that Rust's procedural macros do. Rather, we are proposing a more fine-grained approach that allows macros to make specific changes in an area that are tightly scoped and additive. This is better for compile-time performance, because the compiler doesn't need to expand all macros all the time to understand what the code does. I also feel that it's better for code clarity, because the changes a macro can make are mostly documented up-front, and have a more limited scope.

As for attributes specifically, it's something we can consider, although it's not in any proposal right now. We would need to make sure we can maintain the properties described above if we add the ability for a macro that's written as an attribute to in turn produce more attributes.

Doug

4 Likes

Vote for AttributeMacro, the potential of MemberMacro is huge, it provides a way to adjust members on a large scale, then AttributeMacro is necessary for small-scale filtering of certain members

1 Like

It occurs to me that we might be talking about "attribute macro" as meaning two different things:

  1. A way to have an attached macro that generates other attributes.
  2. A way to have an attached macro that does nothing, but whose presence can be used.

#1 is the one I'm concerned about from a semantic modeling perspective, although I see the utility.

#2 could be modeled by a peer macro that always expands to nothing.

Which of these are you thinking of, or is it both?

Doug

The first one, it is similar to the MemberAttributeMacro, also return [SwiftSyntax.AttributeSyntax], but only works on the marked variable.

Another problem, I see @freestanding(declarations) used here for Codable, but I don't see how DeclarationMacro reads SwiftSyntax.DeclGroupSyntax like MemberMacro does

So @freestanding(declarations) is just used here to give names as an example, right?

Great work :smile: an easy use case could be to obfuscate/deobfuscate constants like API keys and such… looking forward to this :ok_hand:

It should be noted that if an API key is included in your app, obfuscation of it will make no difference and is security through obscurity. Only include an API key in your app if it is a public key such as a client/app id (E.g. the firebase key that you might include in your app). Any other keys should be hidden away on a backend server that your app interacts with through your own API.

If an API key is in your app (obfuscated or not) it can be obtained by anyone who wants to find it.

7 Likes

This would also be helpful for mocking :thinking:, Example:

Usage:

@Mock
override func someMethod() {
}

Result:

var someMethodCallCount = 0
override func someMethod() {
    someMethodCallCount += 1
}

Is replacing declarations something the team is still considering?

Best,
Nuno

Hi, Nuno. You should check out SE-0389 for more up-to-date information about that.

Thank you, John! :pray:

I just read it, but it doesn't mention anything about replacing declarations. It only talks about Attached Macros, which only add additional code, they don't replace existing declarations. :thinking:

You can absolutely think of your macro there as adding code rather than replacing it.

The problem is that for Mocks, adding code is not enough since the goal of a Mock is to replace the original implementation with a dummy one. A Peer Macro could solve this, but it would only work if the Mock is generated in the production code, which is not ideal. Unless there was a way to run a Macro only while Testing :thinking:

Something I’ve been doing with Sourcery for a while is the to have my implementation class, the generate a protocol for it and then a mock for that protocol. That is only adding code not replacing so it should work with macros?

Hi, I'm wondering when adding custom Swift Macro as a Swift Package to my project, will this macro library be embed into my app?

No, the macro binary will be run as a compiler plugin while compiling, but will not be linked into other products (it can even be built for another target platform/architecture, e.g. when you're compiling on macOS for iOS).

With Swift Macros, you can't access the protocol definition if the mock is in a different file. So the only way for that to work is to have the mock in the same file of the protocol/class you are trying to mock. Which is not good since the mocks will be shipped to the app, adding unnecessary size to the app.