<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Xcode » MszPro・株式会社Smartソフト</title>
	<atom:link href="https://mszpro.com/category/xcode/feed" rel="self" type="application/rss+xml" />
	<link>https://mszpro.com</link>
	<description>iOS VisionOS SwiftUI Programming Blog. Dream it, Chase it, Code it.</description>
	<lastBuildDate>Mon, 16 Dec 2024 12:57:25 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.1</generator>

<image>
	<url>https://static-assets.mszpro.com/2024/12/cropped-Unknown-32x32.webp</url>
	<title>Xcode » MszPro・株式会社Smartソフト</title>
	<link>https://mszpro.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Creating custom Swift Macros (with examples SF Symbol / URL validator / iCloud key-value store bind)</title>
		<link>https://mszpro.com/swift-macro</link>
		
		<dc:creator><![CDATA[msz]]></dc:creator>
		<pubDate>Mon, 16 Dec 2024 07:33:14 +0000</pubDate>
				<category><![CDATA[Xcode]]></category>
		<guid isPermaLink="false">https://mszpro.com/?p=314</guid>

					<description><![CDATA[<p>This article will talk about creating your own custom Swift Macro. It will cover 2 types of Macros:&#160;ExpressionMacro&#160;which helps you expand and help you write an expression; and&#160;AccessorMacro&#160;which adds&#160;set&#160;and&#160;get&#160;to a variable. It has some examples: 💡Swift Macro is a new feature announced in WWDC 2023. You will need Xcode 15 to test this. However, this [&#8230;]</p>
<p>The post <a href="https://mszpro.com/swift-macro">Creating custom Swift Macros (with examples SF Symbol / URL validator / iCloud key-value store bind)</a> first appeared on <a href="https://mszpro.com">MszPro・株式会社Smartソフト</a>.</p>]]></description>
										<content:encoded><![CDATA[<p id="8a5e">This article will talk about creating your own custom Swift Macro. It will cover 2 types of Macros:&nbsp;<code>ExpressionMacro</code>&nbsp;which helps you expand and help you write an expression; and&nbsp;<code>AccessorMacro</code>&nbsp;which adds&nbsp;<code>set</code>&nbsp;and&nbsp;<code>get</code>&nbsp;to a variable. It has some examples:</p>



<ul class="wp-block-list">
<li>Verify SF Symbol name and convert it to SwiftUI Image (or throw compile error if name is invalid)</li>



<li>Verifies if String is valid URL and then convert it to URL object (or throw compile time error)</li>



<li>Bind the value of a variable to that on iCloud key-value storage</li>
</ul>



<figure class="wp-block-image"><img fetchpriority="high" decoding="async" width="899" height="425" src="https://static-assets.mszpro.com/2024/12/1K5X5gFVfLhe1VvQY4XU8KA.png" alt="" class="wp-image-318" srcset="https://static-assets.mszpro.com/2024/12/1K5X5gFVfLhe1VvQY4XU8KA-300x142.png 300w, https://static-assets.mszpro.com/2024/12/1K5X5gFVfLhe1VvQY4XU8KA-768x363.png 768w, https://static-assets.mszpro.com/2024/12/1K5X5gFVfLhe1VvQY4XU8KA.png 899w" sizes="(max-width: 899px) 100vw, 899px" /></figure>



<p id="5264">💡Swift Macro is a new feature announced in WWDC 2023. You will need Xcode 15 to test this. However, this is backward compatible with older iOS and MacOS apps (since Xcode expands Macros at compile time)</p>



<h1 class="wp-block-heading" id="1459">Creating a new Macro Swift Package</h1>



<p id="92a4">In the command line, go to the directory you want the package to be located at. Then, type the following:</p>



<pre class="wp-block-code"><code>mkdir ExampleSwiftMacro
cd ExampleSwiftMacro
swift package init --type macro</code></pre>



<p id="6cbd">Now, you can double click on the&nbsp;<code>Package.swift</code>&nbsp;file to open the Swift Package in Xcode.</p>



<p id="a65a">Besides creating this Swift Package from terminal, you can also do it in Xcode by click on the File menu, click on New, click on Package, and select “Swift Macro”</p>



<figure class="wp-block-image"><img decoding="async" width="788" height="573" src="https://static-assets.mszpro.com/2024/12/1MjOgMCyFTKg6Sq4lrcLzSA.png" alt="" class="wp-image-322" srcset="https://static-assets.mszpro.com/2024/12/1MjOgMCyFTKg6Sq4lrcLzSA-300x218.png 300w, https://static-assets.mszpro.com/2024/12/1MjOgMCyFTKg6Sq4lrcLzSA-768x558.png 768w, https://static-assets.mszpro.com/2024/12/1MjOgMCyFTKg6Sq4lrcLzSA.png 788w" sizes="(max-width: 788px) 100vw, 788px" /></figure>



<p>There are 2 files that are important:&nbsp;<code>ExampleSwiftMacro.swift</code>, which provides a definition of the Macro; and the&nbsp;<code>ExampleSwiftMacroMacro.swift</code>&nbsp;file, which provides the implementation of the Macro.</p>



<h1 class="wp-block-heading" id="691d">Implementation of a Macro</h1>



<pre class="wp-block-code"><code>public struct StringifyMacro: ExpressionMacro {
    public static func expansion(
        of node: some FreestandingMacroExpansionSyntax,
        in context: some MacroExpansionContext
    ) -> ExprSyntax {
        guard let argument = node.argumentList.first?.expression else {
            fatalError("compiler bug: the macro does not have any arguments")
        }

        return "(\(argument), \(literal: argument.description))"
    }
}@freestanding(expression)
public macro stringify&lt;T>(_ value: T) -> (T, String) = #externalMacro(module: "ExampleSwiftMacroMacros", type: "StringifyMacro")</code></pre>



<p id="1745">Here, in the example Macro, it tries to read the&nbsp;<code>node.argumentList</code>, which is the input parameters to this Macro.</p>



<p id="18e8">Also, we need to define the macro definition so you can call the above Macro in your code using the # annotation.</p>



<p id="669f">For example, if we call the Macro with the following function:</p>



<pre class="wp-block-code"><code>let (result, code) = #stringify(17 + 25)

print("The value \(result) was produced by the code \"\(code)\"")</code></pre>



<p id="c658">The&nbsp;<code>node.argumentList.first?.expression</code>&nbsp;will be the expression a + b.</p>



<p id="23c9">Now, the output of the above code is:</p>



<pre class="wp-block-code"><code>The value 42 was produced by the code "17 + 25"</code></pre>



<p id="506c">The above output is produced by the return statement of the Macro implementation:</p>



<pre class="wp-block-code"><code>return "(\(argument), \(literal: argument.description))"</code></pre>



<p id="7ed7">Notice that,&nbsp;<code>\(argument)</code>&nbsp;produced the result of the operation&nbsp;<code>17 + 25</code>, and&nbsp;<code>argument.description</code>&nbsp;outputs the original code of that operation.</p>



<h1 class="wp-block-heading" id="769a">Another example</h1>



<p id="4ddd">We can also set up a variable a and b for the above example.</p>



<pre class="wp-block-code"><code>let a = 17
let b = 25

let (result, code) = #stringify(a + b)print("The value \(result) was produced by the code \"\(code)\"")</code></pre>



<p id="4b00">here is the output:</p>



<pre class="wp-block-code"><code>The value 42 was produced by the code "a + b"</code></pre>



<h1 class="wp-block-heading" id="52dc">Defining the list of provided Macros within a package</h1>



<p id="198d">Now, we can add the above Macro to the list of Macros included in this Swift Package:</p>



<pre class="wp-block-code"><code>@main<br>struct ExampleSwiftMacroPlugin: CompilerPlugin {<br>    let providingMacros: &#91;Macro.Type] = &#91;<br>        StringifyMacro.self,<br>    ]<br>}</code></pre>



<h1 class="wp-block-heading" id="a557">Implementing a Macro for converting String to URL</h1>



<p id="0c0f">We can also implement a Macro to convert the String to URL. Normally, we will have to use the following code:</p>



<pre class="wp-block-code"><code>if let url = URL(string: "https://example.com") { ... }</code></pre>



<p id="1ba8">If the URL string is not valid, this will not produce an error in compile time, but it will not work in run time.</p>



<p id="0f2a">However, with the help of Swift Macro, you can check the validity of the URL in compile time. The following code checks if the protocol is&nbsp;<code>http</code>&nbsp;or&nbsp;<code>https</code>, and if there is a valid hostname.</p>



<pre class="wp-block-code"><code>import Foundation
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

public struct URLMacro: ExpressionMacro {
    
    enum URLMacroError: Error {
        case missingArgument
        case argumentNotString
        case invalidURL
        case invalidURL_Scheme
        case invalidURL_Host
    }
    
    public static func expansion(
        of node: some FreestandingMacroExpansionSyntax,
        in context: some MacroExpansionContext
    ) throws -> ExprSyntax {
        guard let argument = node.argumentList.first?.expression else {
            throw URLMacroError.missingArgument
        }
        
        guard let stringLiteralExpr = argument.as(StringLiteralExprSyntax.self),
              let segment = stringLiteralExpr.segments.first?.as(StringSegmentSyntax.self),
              stringLiteralExpr.segments.count == 1
        else {
            throw URLMacroError.argumentNotString
        }
        
        let text = segment.content.text
        
        guard let url = URL(string: text) else {
            throw URLMacroError.invalidURL
        }
        
        guard let scheme = url.scheme,
              &#91;"http", "https"].contains(scheme) else {
            throw URLMacroError.invalidURL_Scheme
        }
        
        guard let host = url.host,
              !host.isEmpty else {
            throw URLMacroError.invalidURL_Host
        }
        
        return #"URL(string: "\#(raw: text)")!"#
    }
    
    private static func isValidURL(_ urlString: String) -> Bool {
        
        
        // You can add more conditions based on your needs
        
        return true
    }
    
}</code></pre>



<p id="1fc2">The above Swift Macro first tries to get the first argument of the input. Then, it checks if the input is a valid String (so if you input an integer to this Macro, Xcode will not compile the code); then, it tries to convert the string to an URL, if it succeeded, it will replace the macro code with URL(string:) code; otherwise, it will not compile.</p>



<p id="f4d3">In the above code, we have also defined custom error types. So when there is an error with the code, Xcode will let us know the error code.</p>



<p id="34bd">Now, we add the&nbsp;<code>macro</code>&nbsp;definition so we can call&nbsp;<code>#urlFromString</code>&nbsp;in our code:</p>



<pre class="wp-block-code"><code>@freestanding(expression)<br>public macro urlFromString&lt;T&gt;(_ value: T) -&gt; (T, String) = #externalMacro(module: "ExampleSwiftMacroMacros", type: "URLMacro")</code></pre>



<p id="d283">Notice that&nbsp;<code>module</code>&nbsp;is the name of the folder which contains the code file for the Macro definition. And&nbsp;<code>type</code>&nbsp;is the name of the struct which conforms to&nbsp;<code>ExpressionMacro</code>.</p>



<p id="c82f">Now, we can call&nbsp;<code>#urlFromString</code>&nbsp;within our code:</p>



<pre class="wp-block-code"><code>let goodURL = #urlFromString("https://apple.com")<br>print(goodURL.host ?? "")</code></pre>



<p id="c8d8">If you provide a wrong URL, the compiler will throw an error (so you will know your code has an invalid URL when compiling):</p>



<figure class="wp-block-image"><img decoding="async" width="947" height="522" src="https://static-assets.mszpro.com/2024/12/17yPftD4T4YnEB-0DROvMww.png" alt="" class="wp-image-319" srcset="https://static-assets.mszpro.com/2024/12/17yPftD4T4YnEB-0DROvMww-300x165.png 300w, https://static-assets.mszpro.com/2024/12/17yPftD4T4YnEB-0DROvMww-768x423.png 768w, https://static-assets.mszpro.com/2024/12/17yPftD4T4YnEB-0DROvMww.png 947w" sizes="(max-width: 947px) 100vw, 947px" /></figure>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="948" height="521" src="https://static-assets.mszpro.com/2024/12/1Rdzvf6i47ZdTILdgqmYJ9w.png" alt="" class="wp-image-320" srcset="https://static-assets.mszpro.com/2024/12/1Rdzvf6i47ZdTILdgqmYJ9w-300x165.png 300w, https://static-assets.mszpro.com/2024/12/1Rdzvf6i47ZdTILdgqmYJ9w-768x422.png 768w, https://static-assets.mszpro.com/2024/12/1Rdzvf6i47ZdTILdgqmYJ9w.png 948w" sizes="auto, (max-width: 948px) 100vw, 948px" /></figure>



<h1 class="wp-block-heading" id="61be">Validate SwiftUI Image SF Symbols at compile time</h1>



<p id="046f">You can validate if SF Symbol code is valid during compile time with a Swift Macro. So, if you enter a SF Symbol code that does not exist, it will throw an error at compile time.</p>



<p id="70c4">First, we will implement the Macro:</p>



<pre class="wp-block-code"><code>public struct SwiftUISystemImageMacro: ExpressionMacro {<br>    <br>    enum SFSymbolMacroError: Error {<br>        case missingArgument<br>        case argumentNotString<br>        case invalidSFSymbolName<br>    }<br>    <br>    public static func expansion(<br>        of node: some FreestandingMacroExpansionSyntax,<br>        in context: some MacroExpansionContext<br>    ) throws -&gt; ExprSyntax {<br>        guard let argument = node.argumentList.first?.expression else {<br>            throw SFSymbolMacroError.missingArgument<br>        }<br>        <br>        guard let stringLiteralExpr = argument.as(StringLiteralExprSyntax.self),<br>              let segment = stringLiteralExpr.segments.first?.as(StringSegmentSyntax.self),<br>              stringLiteralExpr.segments.count == 1<br>        else {<br>            throw SFSymbolMacroError.argumentNotString<br>        }<br>        <br>        let text = segment.content.text<br>        <br>        #if os(iOS)<br>        guard UIImage(systemName: text) != nil else {<br>            throw SFSymbolMacroError.invalidSFSymbolName<br>        }<br>        #elseif os(macOS)<br>        guard NSImage(systemSymbolName: text, accessibilityDescription: nil) != nil else {<br>            throw SFSymbolMacroError.invalidSFSymbolName<br>        }<br>        #endif<br>        <br>        return #"Image(systemName: "\#(raw: text)")"#<br>    }<br>}</code></pre>



<p id="6e4a">The above code checks if the input image name exists in the SF Symbol image (by creating an&nbsp;<code>UIImage</code>&nbsp;or&nbsp;<code>NSImage</code>&nbsp;using that image name); if it does, it will return a SwiftUI Image object with that image; otherwise, it will throw an error.</p>



<p id="ddf0">Now, we add it to the list of Macros provided in the package:</p>



<pre class="wp-block-code"><code>@main<br>struct ExampleSwiftMacroPlugin: CompilerPlugin {<br>    let providingMacros: &#91;Macro.Type] = &#91;<br>        StringifyMacro.self,<br>        URLMacro.self,<br>        SwiftUISystemImageMacro.self<br>    ]<br>}</code></pre>



<p id="bc6f">And we will define a shortcut for this Macro:</p>



<pre class="wp-block-code"><code>@freestanding(expression)<br>public macro systemImage(_ str: String) -&gt; Image = #externalMacro(module: "ExampleSwiftMacroMacros", type: "SwiftUISystemImageMacro")</code></pre>



<p id="07fb">Now, in our SwiftUI view, instead of using Image(systemImage:) — which will show empty image at run time if we enter a wrong image name — we will get error at compile time.</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="845" height="271" src="https://static-assets.mszpro.com/2024/12/1jK1EWbzannIlUHawFFMTEg.png" alt="" class="wp-image-315" srcset="https://static-assets.mszpro.com/2024/12/1jK1EWbzannIlUHawFFMTEg-300x96.png 300w, https://static-assets.mszpro.com/2024/12/1jK1EWbzannIlUHawFFMTEg-768x246.png 768w, https://static-assets.mszpro.com/2024/12/1jK1EWbzannIlUHawFFMTEg.png 845w" sizes="auto, (max-width: 845px) 100vw, 845px" /></figure>



<h1 class="wp-block-heading" id="2fde">Creating a Macro to add modifier to variable (example: access iCloud key-value storage)</h1>



<p id="6e36">You can have a Macro that adds read and write modifiers to a variable so that the value is accessed from iCloud key-value storage</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1176" height="333" src="https://static-assets.mszpro.com/2024/12/14kco4ITtgjhv7BZ2_sBQJg.png" alt="" class="wp-image-321" srcset="https://static-assets.mszpro.com/2024/12/14kco4ITtgjhv7BZ2_sBQJg-300x85.png 300w, https://static-assets.mszpro.com/2024/12/14kco4ITtgjhv7BZ2_sBQJg-1024x290.png 1024w, https://static-assets.mszpro.com/2024/12/14kco4ITtgjhv7BZ2_sBQJg-768x217.png 768w, https://static-assets.mszpro.com/2024/12/14kco4ITtgjhv7BZ2_sBQJg.png 1176w" sizes="auto, (max-width: 1176px) 100vw, 1176px" /></figure>



<p id="fc1c">Above is the image with expanded values. As you can see, this Macro adds the&nbsp;<code>get</code>&nbsp;and&nbsp;<code>set</code>&nbsp;modifier to this variable, and also provides a default value.</p>



<pre class="wp-block-code"><code>public struct NSUbiquitousKeyValueStoreMacro: AccessorMacro {
    
    enum NSUbiquitousKeyValueStoreMacroError: Error {
        case noTypeDefined
        case cannotGetBinding
        case cannotGetVariableName
    }
    
    public static func expansion(of node: AttributeSyntax,
                                 providingAccessorsOf declaration: some DeclSyntaxProtocol,
                                 in context: some MacroExpansionContext) throws -> &#91;AccessorDeclSyntax] {
        
        let typeAttribute = node.attributeName.as(IdentifierTypeSyntax.self)
        guard let dataType = typeAttribute?.type else {
            throw NSUbiquitousKeyValueStoreMacroError.noTypeDefined
        }
        
        guard let varDecl = declaration.as(VariableDeclSyntax.self) else {
            return &#91;]
        }
        
        guard let binding = varDecl.bindings.first?.as(PatternBindingSyntax.self)else {
            throw NSUbiquitousKeyValueStoreMacroError.cannotGetBinding
        }
        
        guard let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier.text else {
            throw NSUbiquitousKeyValueStoreMacroError.cannotGetVariableName
        }
        
        var defaultValue = ""
        if let value = binding.initializer?.value {
            defaultValue = " ?? \(value)"
        }
        
        let getAccessor: AccessorDeclSyntax =
          """
          get {
              (NSUbiquitousKeyValueStore.default.object(forKey: "\(raw: identifier)") as? \(raw: dataType))\(raw: defaultValue)
          }
          """
        
        let setAccessor: AccessorDeclSyntax =
          """
          set {
              NSUbiquitousKeyValueStore.default.set(newValue, forKey: "\(raw: identifier)")
          }
          """
        return &#91;getAccessor, setAccessor]
    }
    
}

extension IdentifierTypeSyntax {
    var type: SyntaxProtocol? {
        genericArgumentClause?.arguments.first?.as(GenericArgumentSyntax.self)?.argument.as(OptionalTypeSyntax.self)?.wrappedType
        ?? genericArgumentClause?.arguments.first?.as(GenericArgumentSyntax.self)
    }
}</code></pre>



<p id="d871">To implement this Macro, we will use an extension&nbsp;<code>IdentifierTypeSyntax.type</code>&nbsp;to read the type we have included in the &lt;&gt; symbol.</p>



<pre class="wp-block-code"><code>extension IdentifierTypeSyntax {
    var type: SyntaxProtocol? {
        genericArgumentClause?.arguments.first?.as(GenericArgumentSyntax.self)?.argument.as(OptionalTypeSyntax.self)?.wrappedType
        ?? genericArgumentClause?.arguments.first?.as(GenericArgumentSyntax.self)
    }
}

let typeAttribute = node.attributeName.as(IdentifierTypeSyntax.self)
guard let dataType = typeAttribute?.type else {
  throw NSUbiquitousKeyValueStoreMacroError.noTypeDefined
}
...</code></pre>



<p id="53c7">Since we have typed&nbsp;<code>@iCloudKeyValue&lt;String&gt;</code>, the type will be&nbsp;<code>String</code></p>



<p id="cc04">The identifier (key of the key-value pair) will be the same as the variable name, which we can read using the following code:</p>



<pre class="wp-block-code"><code>guard let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier.text else {<br>            throw NSUbiquitousKeyValueStoreMacroError.cannotGetVariableName<br>        }</code></pre>



<p id="5f88">Now, you can also read the default value the user provided:</p>



<pre class="wp-block-code"><code>guard let binding = varDecl.bindings.first?.as(PatternBindingSyntax.self)else {
            throw NSUbiquitousKeyValueStoreMacroError.cannotGetBinding
        }

                var defaultValue = ""
        if let value = binding.initializer?.value {
            defaultValue = " ?? \(value)"
        }</code></pre>



<p id="73c5">We will then generate the code for&nbsp;<code>set</code>&nbsp;and&nbsp;<code>get</code>.</p>



<pre class="wp-block-code"><code>let getAccessor: AccessorDeclSyntax =<br>          """<br>          get {<br>              (NSUbiquitousKeyValueStore.default.object(forKey: "\(raw: identifier)") as? \(raw: dataType))\(raw: defaultValue)<br>          }<br>          """<br>        <br>        let setAccessor: AccessorDeclSyntax =<br>          """<br>          set {<br>              NSUbiquitousKeyValueStore.default.set(newValue, forKey: "\(raw: identifier)")<br>          }<br>          """<br>        return &#91;getAccessor, setAccessor]</code></pre>



<h1 class="wp-block-heading" id="92d5">Set a custom compiler error message</h1>



<p id="a1c5">We can also provide a custom compiler error message indicating why the error occurs. For instance, in the above String to URL function, we can show a custom message when the protocol (URL Scheme) is not http or https:</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1039" height="176" src="https://static-assets.mszpro.com/2024/12/1GLZ6k-i56LNYJJfx28HrMw.png" alt="" class="wp-image-317" srcset="https://static-assets.mszpro.com/2024/12/1GLZ6k-i56LNYJJfx28HrMw-300x51.png 300w, https://static-assets.mszpro.com/2024/12/1GLZ6k-i56LNYJJfx28HrMw-1024x173.png 1024w, https://static-assets.mszpro.com/2024/12/1GLZ6k-i56LNYJJfx28HrMw-768x130.png 768w, https://static-assets.mszpro.com/2024/12/1GLZ6k-i56LNYJJfx28HrMw.png 1039w" sizes="auto, (max-width: 1039px) 100vw, 1039px" /></figure>



<p id="debb">First, you need to create an&nbsp;<code>enum</code>&nbsp;with the cases of errors. Notice that the case can contain additional parameter to better describe the issue. For example, if the user has used a wrong protocol, you can include the protocol user used.</p>



<pre class="wp-block-code"><code>public enum CodingKeysMacroDiagnostic {<br>    case missingArgument<br>    case argumentNotString<br>    case invalidURL<br>    case invalidURL_Scheme(String)<br>    case invalidURL_Host<br>}</code></pre>



<p id="cb04">Now, you write an extension of the above&nbsp;<code>enum</code>&nbsp;to provide a function that generates a custom message:</p>



<pre class="wp-block-code"><code>extension CodingKeysMacroDiagnostic: DiagnosticMessage {<br>    func diagnose(at node: some SyntaxProtocol) -&gt; Diagnostic {<br>        Diagnostic(node: Syntax(node), message: self)<br>    }<br>    <br>    public var message: String {<br>        switch self {<br>            case .missingArgument:<br>                return "You need to provide the argument in the parameter"<br>            case .argumentNotString:<br>                return "The argument you provided is not a String"<br>            case .invalidURL:<br>                return "Cannot initialize an URL from your provided string"<br>            case .invalidURL_Scheme(let scheme):<br>                return "\(scheme) is not a supported protocol"<br>            case .invalidURL_Host:<br>                return "The hostname of this URL is invalid"<br>        }<br>    }<br>    <br>    public var severity: DiagnosticSeverity { .error }<br>    <br>    public var diagnosticID: MessageID {<br>        MessageID(domain: "Swift", id: "CodingKeysMacro.\(self)")<br>    }<br>}</code></pre>



<p id="e22c">As you can see, we have defined custom messages by overriding the&nbsp;<code>message</code>property.</p>



<p id="a5d2">Now, in the implementation of the Swift Macro, we will add the code line to return a diagnostic information to the context:</p>



<pre class="wp-block-code"><code>public struct URLMacro: ExpressionMacro {<br>    <br>    enum URLMacroError: Error {<br>        case missingArgument<br>        case argumentNotString<br>        case invalidURL<br>        case invalidURL_Scheme<br>        case invalidURL_Host<br>    }<br>    <br>    public static func expansion(<br>        of node: some FreestandingMacroExpansionSyntax,<br>        in context: some MacroExpansionContext<br>    ) throws -&gt; ExprSyntax {<br>        guard let argument = node.argumentList.first?.expression else {<br>            context.diagnose(CodingKeysMacroDiagnostic.missingArgument.diagnose(at: node))<br>            throw URLMacroError.missingArgument<br>        }<br>        <br>        guard let stringLiteralExpr = argument.as(StringLiteralExprSyntax.self),<br>              let segment = stringLiteralExpr.segments.first?.as(StringSegmentSyntax.self),<br>              stringLiteralExpr.segments.count == 1<br>        else {<br>            context.diagnose(CodingKeysMacroDiagnostic.argumentNotString.diagnose(at: node))<br>            throw URLMacroError.argumentNotString<br>        }<br>        <br>        let text = segment.content.text<br>        <br>        guard let url = URL(string: text) else {<br>            context.diagnose(CodingKeysMacroDiagnostic.invalidURL.diagnose(at: node))<br>            throw URLMacroError.invalidURL<br>        }<br>        <br>        guard let scheme = url.scheme,<br>              &#91;"http", "https"].contains(scheme) else {<br>            context.diagnose(CodingKeysMacroDiagnostic.invalidURL_Scheme(url.scheme ?? "?").diagnose(at: node))<br>            throw URLMacroError.invalidURL_Scheme<br>        }<br>        <br>        guard let host = url.host,<br>              !host.isEmpty else {<br>            context.diagnose(CodingKeysMacroDiagnostic.invalidURL_Host.diagnose(at: node))<br>            throw URLMacroError.invalidURL_Host<br>        }<br>        <br>        return #"URL(string: "\#(raw: text)")!"#<br>    }<br>    <br>}</code></pre>



<p id="c89f">Now, when you test it, you will see the diagnostic message.</p>



<h1 class="wp-block-heading" id="e042">Other uses of Macro</h1>



<p id="69cf">This article introduces 2 types of Macro,&nbsp;<code>ExpressionMacro</code>&nbsp;which helps you complete an expression, and&nbsp;<code>AccessorMacro</code>, which helps to add modifiers to a variable.</p>



<p id="0302">Here is the source code for this article:</p>



<p id="54e9"><a href="https://github.com/mszpro/Swift_Macro_Qiita" rel="noreferrer noopener" target="_blank">GitHub — mszpro/Swift_Macro_Qiita: Code for the Qiita article on Swift…https://github.com</a></p>



<p id="e2ac">There are other types of Macros:</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="197" height="307" src="https://static-assets.mszpro.com/2024/12/1piGOC_jrSrP5ypQJ1D_G8w.png" alt="" class="wp-image-316" srcset="https://static-assets.mszpro.com/2024/12/1piGOC_jrSrP5ypQJ1D_G8w-193x300.png 193w, https://static-assets.mszpro.com/2024/12/1piGOC_jrSrP5ypQJ1D_G8w.png 197w" sizes="auto, (max-width: 197px) 100vw, 197px" /></figure>



<p id="1958">There are also some interesting Github repositories online where you can learn more about using Macros:</p>



<p id="45f6"><a href="https://github.com/Ryu0118/CodingKeysMacro" rel="noreferrer noopener" target="_blank">GitHub — Ryu0118/CodingKeysMacro</a></p>



<p id="ea5d"><a href="https://github.com/LeonardoCardoso/InitMacro" rel="noreferrer noopener" target="_blank">GitHub — LeonardoCardoso/InitMacro</a></p>



<p id="5b8d">Thanks for reading!</p><p>The post <a href="https://mszpro.com/swift-macro">Creating custom Swift Macros (with examples SF Symbol / URL validator / iCloud key-value store bind)</a> first appeared on <a href="https://mszpro.com">MszPro・株式会社Smartソフト</a>.</p>]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/

Page Caching using Disk: Enhanced 
Lazy Loading (feed)
Database Caching 5/74 queries in 0.032 seconds using Disk (Request-wide modification query)

Served from: mszpro.com @ 2025-07-08 14:31:50 by W3 Total Cache
-->