I have a question while studying Swift Macros.
When a specific type wants to conform to a custom protocol in Swift Macros, should that custom protocol be declared within the Macro Package? Or can it be declared in the main app project?
For example, if the @ServiceProvidable macro makes a specific class conform to the ServiceProvidable protocol and expands the code with a computed property, how should it be implemented?
@ServiceProvidable
class ViewControllerWrapper {
}
// Expanding a macro
class ViewControllerWrapper { }
extension ViewControllerWrapper: ServiceProvidable {
var service: ServiceProviderProtocol {
guard
let serviceProvider = (UIApplication.shared.delegate as? AppDelegate)?.serviceProvider
else { return ServiceProvider() }
return serviceProvider
}
}
You need to specify the conformances that a macro provides, when declaring the macro. e.g.
@attached(extension, conformances: FooProtocol) // You need to be able to say "FooProtocol" here
public macro MyMacro() = #externalMacro(module: "SomeImplementationModule", type: "MyMacro")
Therefore, the protocol should be declared in a module that the code above can access, so either in the same module as the above code, or one of its dependencies. I usually just put it right next to the macro declaration.
@attached(extension, conformances: FooProtocol)
public macro MyMacro() = #externalMacro(module: "SomeImplementationModule", type: "MyMacro")
public protocol FooProtocol {
func foo()
}
Note that this is not the module where you implement the macro.
You should implement the macro in another module (in the above example, in a module called "SomeImplementationModule"), created using .macro(...)
instead of .target(...)
in Package.swift.
Here is an example implementation:
enum MyMacro: ExtensionMacro {
static func expansion(
of node: AttributeSyntax,
attachedTo declaration: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
let name = declaration.as(ClassDeclSyntax.self)!.name
return [
try ExtensionDeclSyntax("extension \(name): FooProtocol") {
"""
public func foo() { print("example implementation") }
"""
}
]
}
}
@main
struct MyMacroPlugin: CompilerPlugin {
let providingMacros: [Macro.Type] = [
MyMacro.self
]
}