ios – Swift ExtensionMacro – Stack Overflow


I am trying to create a macro to aid in testing on a production app and have been trying a fair few different things. More detail:

Desired Outcome:

In the project we have a lot of protocols, so given the below:

protocol Foo {
    var bar: Bool { get }
    func fooBar() -> Bool
}

We want to create a @Testable that would we would use like so:

@Testable
final class FooSpy: Foo { }

This class would be defined in the Testing bundle and we would expect the macro to expand into:

final class FooSpy: Foo {

    var bar: Bool


    var fooBarExpectation = XCTestExpectation()
    var fooBarToReturn: Bool = false
    func fooBar() -> Bool {
        fooBarExpectation.fullFill()
        return fooBarToReturn
    }

}

Approach

I have managed to create a PeerMacro that does the above by setting the protocol as @Testable. The issue is that the macro expands in place where the protocol is and as such various testing related code doesn’t compile due to lack of imports etc.

I then went on to try and make an ExtensionMacro so that I could mark the Test class as @Testable which is inside the test target and thus has the required imports. The issue is, that with this approach I don’t seem to get access to the Protocols conformance requirements. Code below

public struct Testable: ExtensionMacro {
    private static let extractor = Extractor()

    public static func expansion(of node: SwiftSyntax.AttributeSyntax, 
                                 attachedTo declaration: some SwiftSyntax.DeclGroupSyntax,
                                 providingExtensionsOf type: some SwiftSyntax.TypeSyntaxProtocol,
                                 conformingTo protocols: [SwiftSyntax.TypeSyntax],
                                 in context: some SwiftSyntaxMacros.MacroExpansionContext) throws -> [SwiftSyntax.ExtensionDeclSyntax] {
        
// Here I'm trying to get the protocol details (extractor below):
let protocolDeclaration = try extractor.extractProtocolDeclaration(from: declaration)

Extractor:

struct Extractor {
  func extractClassDeclaration(
    from declaration: DeclGroupSyntax
  ) throws -> ProtocolDeclSyntax {

    guard let protocolDeclaration = declaration.as(ProtocolDeclSyntax) else {
        throw TestableMacroError.invalidProtocol
    }

    return protocolDeclaration
  }
}

The obvious issue is that with the ExtensionMacro, the declaration is no longer the same as when it’s a PeerMacro so this code doesn’t work.

How can I (if i even can) get the protocol details from a DeclGroupSyntax?

Latest articles

spot_imgspot_img

Related articles

Leave a reply

Please enter your comment!
Please enter your name here

spot_imgspot_img