Reputation: 10608
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:
do you know of a built-in Swift function that already provides such functionality? I am a seasoned Swift developer and I have never heard of such thing, I have also searched the docs, Google and StackOverflow, to no avail — and no, dump() does not do the job
otherwise, do you know how to get the variable name in a string form?
Mirroring or KeyPaths are not suitable, they only address property names of a declared type, not a variable's name. I also don't want to change anything to my code architecture to be able to just add a log, so declaring a struct for that purpose is not a valid answer.
I have also searched for a Macro, but I only have minimal knowledge of their possibilities.
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
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