KPM
KPM

Reputation: 10608

How do I print the name of a variable in Swift?

I often find myself typing debug prints like print("myVar: \(myVar)") and when I want to print several variables like that it becomes tedious to write.

I would like to write (or use, if it's built-in) a function that only requires me to type the name once, like printVar(myVar) and it would print myVar: 20 if myVar's value is 20.

So my questions are:


If I have that, I can even write a variadic function and then just type a concise

printVars(myVar, myOtherVar, myThirdVar)

and get a nice log, which would reaaaally be a time saver:

myVar: 20
myOtherVar: MyEnum.myCase
myThirdVar: "Arthur"

Upvotes: 3

Views: 347

Answers (1)

Sweeper
Sweeper

Reputation: 271380

If you are fine with using a macro, you can write a freestanding expression macro like this:

public struct PrintWithVariableName: ExpressionMacro {
    public static func expansion(
        of node: some FreestandingMacroExpansionSyntax,
        in context: some MacroExpansionContext
    ) throws -> ExprSyntax {
        let argExpressions = node.argumentList.map(\.expression)
        let printArgs = LabeledExprListSyntax {
            for expression in argExpressions {
                LabeledExprSyntax(
                    expression: StringLiteralExprSyntax(content: expression.trimmedDescription + ":")
                )
                LabeledExprSyntax(expression: expression)
            }
        }
        return "print(\(printArgs))"
    }

    /*
    // an alternative implementation that expands to a closure that is invoked immediately,
    // containing a print call for each expression, so that they are printed on separate lines
    public static func expansion(
        of node: some FreestandingMacroExpansionSyntax,
        in context: some MacroExpansionContext
    ) throws -> ExprSyntax {
        let argExpressions = node.argumentList.map(\.expression)
        let block = CodeBlockItemListSyntax {
            for expression in argExpressions {
                "print(\(literal: expression.trimmedDescription + ":"), \(expression))"
            }
        }
        return """
        {
            \(block)
        }()
        """
    }
    */
}

Add this to the providingMacros list, and declare it like this:

@freestanding(expression)
public macro printWithNames(_ args: Any...) = 
    #externalMacro(module: "...", type: "PrintWithVariableName")

Usage:

#printWithNames(x, y)
// expands to:
print("x:", x, "y:", y)

Upvotes: 5

Related Questions