Exploring Freestanding Declaration Macros in Swift

VINODH KUMAR
3 min readFeb 24, 2024

--

Photo by Abdelrahman Sobhy on Unsplash

Macros in Swift are incredibly versatile tools that enable developers to automate repetitive tasks and enhance code readability and maintainability. Among the various types of macros available, Freestanding Declaration Macros stand out for their ability to autonomously generate new types of declarations, such as functions, variables, or types, based on custom-defined logic within the macro’s definition. In this article, we’ll take a deep dive into understanding Freestanding Declaration Macros through a practical example.

Understanding Freestanding Declaration Macros

Freestanding Declaration Macros are designed to expand into new declarations without relying on any specific context within the Swift codebase. Unlike other macros that might require certain conditions or contexts to be met, Freestanding Declaration Macros operate independently, allowing developers to create custom declarations with arbitrary logic.

Macro Definition

Let’s consider a scenario where we want to create a Freestanding Declaration Macro that generates a unique method within a custom class. Here’s how we can define such a macro:

@freestanding(declaration, names: named(MyClass))
public macro FuncUnique() = #externalMacro(module: "MacroExamplesImplementation", type: "FuncUniqueMacro")

In this macro definition:

  • @freestanding(declaration, names: named(MyClass)) specifies that this macro is freestanding and acts as a declaration. It also indicates that the generated declaration will be associated with the MyClass type.
  • public macro FuncUnique() defines the macro named FuncUnique, which will generate a unique method within a class.
  • = #externalMacro(module: “MacroExamplesImplementation”, type: “FuncUniqueMacro”) assigns the implementation of the macro to an external module named MacroExamplesImplementation where the FuncUniqueMacro type resides.

Macro Implementation

Now, let’s take a look at the implementation of the FuncUniqueMacro, which defines the logic for generating the unique method:

import SwiftCompilerPlugin
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

/// Func With unique name.
public enum FuncUniqueMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
print(node.argumentList)
print(context)
let name = context.makeUniqueName("unique")
print(name)
return [
"""
class MyClass {
func \(name)() {}
}
"""
]
}
}

In this implementation:

  • We import necessary modules such as SwiftSyntax and SwiftSyntaxMacros.
  • The FuncUniqueMacro type conforms to the DeclarationMacro protocol, indicating that it’s responsible for generating declarations.
  • The expansion function takes an AST (Abstract Syntax Tree) node representing the macro expansion and a context object providing information about the expansion process.
  • Inside the expansion function, we generate a unique method name using the makeUniqueName method provided by the context object.
  • We then construct a class declaration with the unique method using SwiftSyntaxBuilder.

Example Usage

Finally, let’s see how we can use the FuncUnique macro in our code:

import MacroExamplesInterface
// MARK: - Func Unique
#FuncUnique

// Expanded code when you do right click and expand macro in xcode
/*
class MyClass {
func $s22DeclarationMacroClient03_F8D28BC059F4523B96C95750FD5F825D2Ll10FuncUniquefMf0_6uniquefMu_() {
}
}
*/

func runFuncUniqueMacroPlayground() {
print("My Class Declaration with unique method: ", MyClass())
}

In this example:

  • We import the module MacroExamplesInterface where the FuncUnique macro is defined.
  • We use the #FuncUnique syntax to apply the macro, which will generate a class declaration with a unique method.
  • We define a function runFuncUniqueMacroPlayground to demonstrate the usage of the generated class with the unique method.

Conclusion

Freestanding Declaration Macros in Swift are potent tools that offer developers unparalleled flexibility in code generation. By allowing for the autonomous creation of new declarations, these macros streamline repetitive tasks and enhance code maintainability. Through a deep dive into their definition and implementation, we’ve witnessed how Freestanding Declaration Macros empower developers to craft custom declarations with ease, ultimately leading to more efficient and robust software development processes.

--

--

VINODH KUMAR

📱 Senior iOS Developer | Swift Enthusiast | Tech Blogger 🖥️ Connect with me on linkedin.com/in/vinodhkumar-govindaraj-838a85100