Last active
July 26, 2023 17:49
-
-
Save megabitsenmzq/f3f26d0f8ed512b819f83ee7e30cf713 to your computer and use it in GitHub Desktop.
A macro that saves the variable to user defaults.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Usage | |
@SavedInDefaults(defaultValue: "foo", group: "group.foo", namespace: "bar") | |
var fooBar: String | |
// Expanded --- | |
{ | |
get { | |
if let savedValue = UserDefaults(suiteName: "group.foo")!.object(forKey: "bar.fooBar") as? String { | |
return savedValue | |
} else { | |
return "foo" | |
} | |
} | |
set { | |
UserDefaults(suiteName: "group.foo")!.set(newValue, forKey: "bar.fooBar") | |
} | |
} | |
// --- | |
// Macro | |
@attached(accessor) | |
public macro SavedInDefaults<T>(defaultValue: T, group: String = "", namespace: String = "") = #externalMacro(module: "HSCalculatorMacroMacros", type: "SavedInDefaultsMacro") | |
// CompilerPlugin | |
import SwiftCompilerPlugin | |
import SwiftSyntax | |
import SwiftSyntaxBuilder | |
import SwiftSyntaxMacros | |
public struct SavedInDefaultsMacro: AccessorMacro { | |
public static func expansion< | |
Context: MacroExpansionContext, | |
Declaration: DeclSyntaxProtocol | |
>( | |
of node: AttributeSyntax, | |
providingAccessorsOf declaration: Declaration, | |
in context: Context | |
) throws -> [AccessorDeclSyntax] { | |
guard let varDecl = declaration.as(VariableDeclSyntax.self), | |
let binding = varDecl.bindings.first, | |
let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier, | |
binding.accessor == nil, | |
let type = binding.typeAnnotation?.type | |
else { | |
return [] | |
} | |
var defaults = "UserDefaults.standard" | |
var namespace = "" | |
guard case let .argumentList(arguments) = node.argument else { | |
fatalError("No arguments") | |
} | |
if let stringLiteral = arguments.map({$0})[1].expression.as(StringLiteralExprSyntax.self), | |
case let .stringSegment(newGroupID)? = stringLiteral.segments.first { | |
let text = newGroupID.content.text | |
if text != "" { | |
defaults = "UserDefaults(suiteName: \"\(newGroupID.content.text)\")!" | |
} | |
} | |
if let stringLiteral = arguments.map({$0})[2].expression.as(StringLiteralExprSyntax.self), | |
case let .stringSegment(newNameSpace)? = stringLiteral.segments.first { | |
let text = newNameSpace.content.text | |
if text != "" { | |
namespace = newNameSpace.content.text + "." | |
} | |
} | |
let defaultValue = arguments.map({$0})[0].expression | |
return [ | |
""" | |
get { | |
if let savedValue = \(raw: defaults).object(forKey: \(literal: namespace + identifier.text)) as? \(type) { | |
return savedValue | |
} else { | |
return \(defaultValue) | |
} | |
} | |
""", | |
""" | |
set { | |
\(raw: defaults).set(newValue, forKey: \(literal: namespace + identifier.text)) | |
} | |
""", | |
] | |
} | |
} | |
@main | |
struct HSCalculatorMacroPlugin: CompilerPlugin { | |
let providingMacros: [Macro.Type] = [ | |
SavedInDefaultsMacro.self, | |
] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment