Prepare the Data First
Process any data that the renderer functions will need separately from any code that touches SwiftSyntax. Any fetching, parsing, mapping should be done outside the renderer. The renderer functions should just take a model with everything neatly arranged for it and output the rendered code.
This helps keep this code simpler, because it’s only about the structure of the source code you’re building.
Use Extensions to Simplify
If you find you have a few lines of SwiftSyntax code just to do something very simple, then extract it into an extension, especially if it’s repeated in a few places.
Make these extensions private, they should be specific to your implementation here and won’t usually be re-usable in the rest of the project.
When writing extensions consider using concrete types for the arguments, like
TypeSyntax, instead of protocols like
TypeSyntaxProtocol. This will allow you to leverage
ExpressibleByStringLiteral so you can just pass a string at the call-site.
Here’s an example — I often want to tag a struct as conforming to a protocol, so I added this extension, and now I can call
ExtensionDeclSyntax.conform("MyStruct", to: "Equatable") to do it in one line of easily understood code.
Isolate SwiftSyntax to One File
SwiftSyntax quarantined. For public functions, don’t use any SwiftSyntax types as parameters or return values. I have a single ~250 line file with
import SwiftSyntax in my project and it contains everything I need to understand how rendering is working.
Find a Balance when Specifying Syntax
Don’t go to extremes to specify everything as granularly as possible. When rendering a utility function with no interpolation in it, favour using
DeclSyntax() with a block string.
Conversely, if you have big block string with a lot of interpolation happening, consider breaking it up into more explicit pieces of syntax.