View in English

  • Apple Developer
    • Get Started

    Explore Get Started

    • Overview
    • Learn
    • Apple Developer Program

    Stay Updated

    • Latest News
    • Hello Developer
    • Platforms

    Explore Platforms

    • Apple Platforms
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    • App Store

    Featured

    • Design
    • Distribution
    • Games
    • Accessories
    • Web
    • Home
    • CarPlay
    • Technologies

    Explore Technologies

    • Overview
    • Xcode
    • Swift
    • SwiftUI

    Featured

    • Accessibility
    • App Intents
    • Apple Intelligence
    • Games
    • Machine Learning & AI
    • Security
    • Xcode Cloud
    • Community

    Explore Community

    • Overview
    • Meet with Apple events
    • Community-driven events
    • Developer Forums
    • Open Source

    Featured

    • WWDC
    • Swift Student Challenge
    • Developer Stories
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Centers
    • Documentation

    Explore Documentation

    • Documentation Library
    • Technology Overviews
    • Sample Code
    • Human Interface Guidelines
    • Videos

    Release Notes

    • Featured Updates
    • iOS
    • iPadOS
    • macOS
    • watchOS
    • visionOS
    • tvOS
    • Xcode
    • Downloads

    Explore Downloads

    • All Downloads
    • Operating Systems
    • Applications
    • Design Resources

    Featured

    • Xcode
    • TestFlight
    • Fonts
    • SF Symbols
    • Icon Composer
    • Support

    Explore Support

    • Overview
    • Help Guides
    • Developer Forums
    • Feedback Assistant
    • Contact Us

    Featured

    • Account Help
    • App Review Guidelines
    • App Store Connect Help
    • Upcoming Requirements
    • Agreements and Guidelines
    • System Status
  • Quick Links

    • Events
    • News
    • Forums
    • Sample Code
    • Videos
 

Vidéos

Ouvrir le menu Fermer le menu
  • Collections
  • Toutes les vidéos
  • À propos

Plus de vidéos

  • À propos
  • Code
  • Expand on Swift macros

    Discover how Swift macros can help you reduce boilerplate in your codebase and adopt complex features more easily. Learn how macros can analyze code, emit rich compiler errors to guide developers towards correct usage, and generate new code that is automatically incorporated back into your project. We'll also take you through important concepts like macro roles, compiler plugins, and syntax trees.

    Chapitres

    • 0:00 - Introduction
    • 0:51 - Why macros?
    • 2:13 - Design philosophy
    • 4:48 - Translation model
    • 6:18 - Macro roles
    • 17:48 - Macro implementation
    • 33:36 - Writing correct macros
    • 38:42 - Wrap up

    Ressources

      • Vidéo HD
      • Vidéo SD

    Vidéos connexes

    WWDC23

    • Discover Observation in SwiftUI
    • Swift 的新功能
    • What’s new in Xcode 15
    • Write Swift macros
  • Rechercher dans cette vidéo…
    • 0:44 - The #unwrap expression macro, with a more complicated argument

      let image = #unwrap(request.downloadedImage, message: "was already checked")
      
                  // Begin expansion for "#unwrap"
                  { [wrappedValue = request.downloadedImage] in
                      guard let wrappedValue else {
                          preconditionFailure(
                              "Unexpectedly found nil: ‘request.downloadedImage’ " + 
                                  "was already checked",
                              file: "main/ImageLoader.swift",
                              line: 42
                          )
                      }
                      return wrappedValue
                  }()
                  // End expansion for "#unwrap"
    • 0:50 - Existing features using expansions (1)

      struct Smoothie: Codable {
          var id, title, description: String
          var measuredIngredients: [MeasuredIngredient]
      
          static let berryBlue =
              Smoothie(id: "berry-blue", title: "Berry Blue") {
                  """
                  Filling and refreshing, this smoothie \
                  will fill you with joy!
                  """
      
                  Ingredient.orange
                      .measured(with: .cups).scaled(by: 1.5)
                  Ingredient.blueberry
                      .measured(with: .cups)
                  Ingredient.avocado
                      .measured(with: .cups).scaled(by: 0.2)
              }
      }
    • 1:11 - Existing features using expansions (2)

      struct Smoothie: Codable {
          var id, title, description: String
          var measuredIngredients: [MeasuredIngredient]
      
              // Begin expansion for Codable
              private enum CodingKeys: String, CodingKey {
                  case id, title, description,
                       measuredIngredients
              }
      
              init(from decoder: Decoder) throws { … }
      
              func encode(to encoder Encoder) throws { … }
              // End expansion for Codable
      
          static let berryBlue =
              Smoothie(id: "berry-blue", title: "Berry Blue") {
                  """
                  Filling and refreshing, this smoothie \
                  will fill you with joy!
                  """
      
                  Ingredient.orange
                      .measured(with: .cups).scaled(by: 1.5)
                  Ingredient.blueberry
                      .measured(with: .cups)
                  Ingredient.avocado
                      .measured(with: .cups).scaled(by: 0.2)
              }
      }
    • 3:16 - Macros inputs are complete, type-checked, and validated

      #unwrap(1 + )    // error: expected expression after operator
      
      
      
      
      @AddCompletionHandler(parameterName: 42)    // error: cannot convert argument of type 'Int' to expected type 'String'
      func sendRequest() async throws -> Response
      
      
      
      
      @DictionaryStorage class Options { … }    // error: '@DictionaryStorage' can only be applied to a 'struct'
    • 3:45 - Macro expansions are inserted in predictable ways

      func doThingy() {
          startDoingThingy()
      
          #someUnknownMacro()
      
          finishDoingThingy()
      }
    • 4:51 - How macros work, featuring #stringify

      func printAdd(_ a: Int, _ b: Int) {
          let (result, str) = #stringify(a + b)
        
              // Begin expansion for "#stringify"
              (a + b, "a + b")
              // End expansion for "#stringify"
        
          print("\(str) = \(result)")
      }
      
      printAdd(1, 2)    // prints "a + b = 3"
    • 5:43 - Macro declaration for #stringify

      /// Creates a tuple containing both the result of `expr` and its source code represented as a
      /// `String`.
      @freestanding(expression)
      macro stringify<T>(_ expr: T) -> (T, String)
    • 7:11 - What’s an expression?

      let numPixels = (x + width) * (y + height)
      //              ^~~~~~~~~~~~~~~~~~~~~~~~~~ This is an expression
      //               ^~~~~~~~~                 But so is this
      //                   ^~~~~                 And this
    • 7:34 - The #unwrap expression macro: motivation

      // Some teams are nervous about this:
      let image = downloadedImage!
      
      // Alternatives are super wordy:
      guard let image = downloadedImage else {
          preconditionFailure("Unexpectedly found nil: downloadedImage was already checked")
      }
    • 8:03 - The #unwrap expression macro: macro declaration

      /// Force-unwraps the optional value passed to `expr`.
      /// - Parameter message: Failure message, followed by `expr` in single quotes
      @freestanding(expression)
      macro unwrap<Wrapped>(_ expr: Wrapped?, message: String) -> Wrapped
    • 8:21 - The #unwrap expression macro: usage

      let image = #unwrap(downloadedImage, message: "was already checked")
      
                  // Begin expansion for "#unwrap"
                  { [downloadedImage] in
                      guard let downloadedImage else {
                          preconditionFailure(
                              "Unexpectedly found nil: ‘downloadedImage’ " + "was already checked",
                              file: "main/ImageLoader.swift",
                              line: 42
                          )
                      }
                      return downloadedImage
                  }()
                  // End expansion for "#unwrap"
    • 9:09 - The #makeArrayND declaration macro: motivation

      public struct Array2D<Element>: Collection {
          public struct Index: Hashable, Comparable { var storageIndex: Int }
        
          var storage: [Element]
          var width1: Int
        
          public func makeIndex(_ i0: Int, _ i1: Int) -> Index {
              Index(storageIndex: i0 * width1 + i1)
          }
        
          public subscript (_ i0: Int, _ i1: Int) -> Element {
              get { self[makeIndex(i0, i1)] }
              set { self[makeIndex(i0, i1)] = newValue }
          }
      
          public subscript (_ i: Index) -> Element {
              get { storage[i.storageIndex] }
              set { storage[i.storageIndex] = newValue }
          }
      
          // Note: Omitted additional members needed for 'Collection' conformance
      }
      
      public struct Array3D<Element>: Collection {
          public struct Index: Hashable, Comparable { var storageIndex: Int }
        
          var storage: [Element]
          var width1, width2: Int
        
          public func makeIndex(_ i0: Int, _ i1: Int, _ i2: Int) -> Index {
              Index(storageIndex: (i0 * width1 + i1) * width2 + i2)
          }
        
          public subscript (_ i0: Int, _ i1: Int, _ i2: Int) -> Element {
              get { self[makeIndex(i0, i1, i2)] }
              set { self[makeIndex(i0, i1, i2)] = newValue }
          }
        
          public subscript (_ i: Index) -> Element {
              get { storage[i.storageIndex] }
              set { storage[i.storageIndex] = newValue }
          }
        
          // Note: Omitted additional members needed for 'Collection' conformance
      }
    • 10:03 - The #makeArrayND declaration macro: macro declaration

      /// Declares an `n`-dimensional array type named `Array<n>D`.
      /// - Parameter n: The number of dimensions in the array.
      @freestanding(declaration, names: arbitrary)
      macro makeArrayND(n: Int)
    • 10:15 - The #makeArrayND declaration macro: usage

      #makeArrayND(n: 2)
      
      // Begin expansion for "#makeArrayND"
      public struct Array2D<Element>: Collection {
          public struct Index: Hashable, Comparable { var storageIndex: Int }
          var storage: [Element]
          var width1: Int
          public func makeIndex(_ i0: Int, _ i1: Int) -> Index {
              Index(storageIndex: i0 * width1 + i1)
          }
          public subscript (_ i0: Int, _ i1: Int) -> Element {
              get { self[makeIndex(i0, i1)] }
              set { self[makeIndex(i0, i1)] = newValue }
          }
          public subscript (_ i: Index) -> Element {
              get { storage[i.storageIndex] }
              set { storage[i.storageIndex] = newValue }
          }
      }
      // End expansion for "#makeArrayND"
      
      #makeArrayND(n: 3)
      #makeArrayND(n: 4)
      #makeArrayND(n: 5)
    • 11:23 - The @AddCompletionHandler peer macro: motivation

      /// Fetch the avatar for the user with `username`.
      func fetchAvatar(_ username: String) async -> Image? {
          ...
      }
      
      func fetchAvatar(_ username: String, onCompletion: @escaping (Image?) -> Void) {
          Task.detached { onCompletion(await fetchAvatar(username)) }
      }
    • 11:51 - The @AddCompletionHandler peer macro: macro declaration

      /// Overload an `async` function to add a variant that takes a completion handler closure as
      /// a parameter.
      @attached(peer, names: overloaded)
      macro AddCompletionHandler(parameterName: String = "completionHandler")
    • 11:59 - The @AddCompletionHandler peer macro: usage

      /// Fetch the avatar for the user with `username`.
      @AddCompletionHandler(parameterName: "onCompletion")
      func fetchAvatar(_ username: String) async -> Image? {
          ...
      }
      
          // Begin expansion for "@AddCompletionHandler"
      
          /// Fetch the avatar for the user with `username`.
          /// Equivalent to ``fetchAvatar(username:)`` with
          /// a completion handler.
          func fetchAvatar(
              _ username: String,
              onCompletion: @escaping (Image?) -> Void
          ) {
              Task.detached {
                  onCompletion(await fetchAvatar(username))
              }
          }
      
          // End expansion for "@AddCompletionHandler"
    • 12:36 - The @DictionaryStorage accessor macro: motivation

      struct Person: DictionaryRepresentable {
          init(dictionary: [String: Any]) { self.dictionary = dictionary }
          var dictionary: [String: Any]
        
          var name: String {
              get { dictionary["name"]! as! String }
              set { dictionary["name"] = newValue }
          }
          var height: Measurement<UnitLength> {
              get { dictionary["height"]! as! Measurement<UnitLength> }
              set { dictionary["height"] = newValue }
          }
          var birthDate: Date? {
              get { dictionary["birth_date"] as! Date? }
              set { dictionary["birth_date"] = newValue as Any? }
          }
      }
    • 13:04 - The @DictionaryStorage accessor macro: declaration

      /// Adds accessors to get and set the value of the specified property in a dictionary
      /// property called `storage`.
      @attached(accessor)
      macro DictionaryStorage(key: String? = nil)
    • 13:20 - The @DictionaryStorage accessor macro: usage

      struct Person: DictionaryRepresentable {
          init(dictionary: [String: Any]) { self.dictionary = dictionary }
          var dictionary: [String: Any]
          
          @DictionaryStorage var name: String
              // Begin expansion for "@DictionaryStorage"
              {
                  get { dictionary["name"]! as! String }
                  set { dictionary["name"] = newValue }
              }
              // End expansion for "@DictionaryStorage"
      
          @DictionaryStorage var height: Measurement<UnitLength>
              // Begin expansion for "@DictionaryStorage"
              {
                  get { dictionary["height"]! as! Measurement<UnitLength> }
                  set { dictionary["height"] = newValue }
              }
              // End expansion for "@DictionaryStorage"
      
          @DictionaryStorage(key: "birth_date") var birthDate: Date?
              // Begin expansion for "@DictionaryStorage"
              {
                  get { dictionary["birth_date"] as! Date? }
                  set { dictionary["birth_date"] = newValue as Any? }
              }
              // End expansion for "@DictionaryStorage"
      }
    • 13:56 - The @DictionaryStorage member attribute macro: macro declaration

      /// Adds accessors to get and set the value of the specified property in a dictionary
      /// property called `storage`.
      @attached(memberAttribute)
      @attached(accessor)
      macro DictionaryStorage(key: String? = nil)
    • 14:46 - The @DictionaryStorage member attribute macro: usage

      @DictionaryStorage
      struct Person: DictionaryRepresentable {
          init(dictionary: [String: Any]) { self.dictionary = dictionary }
          var dictionary: [String: Any]
          
              // Begin expansion for "@DictionaryStorage"
              @DictionaryStorage
              // End expansion for "@DictionaryStorage"
          var name: String
          
              // Begin expansion for "@DictionaryStorage"
              @DictionaryStorage
              // End expansion for "@DictionaryStorage"
          var height: Measurement<UnitLength>
      
          @DictionaryStorage(key: "birth_date") var birthDate: Date?
      }
    • 15:52 - The @DictionaryStorage member macro: macro definition

      /// Adds accessors to get and set the value of the specified property in a dictionary
      /// property called `storage`.
      @attached(member, names: named(dictionary), named(init(dictionary:)))
      @attached(memberAttribute)
      @attached(accessor)
      macro DictionaryStorage(key: String? = nil)
    • 16:26 - The @DictionaryStorage member macro: usage

      // The @DictionaryStorage member macro
      
      @DictionaryStorage struct Person: DictionaryRepresentable {
              // Begin expansion for "@DictionaryStorage"
              init(dictionary: [String: Any]) {
                  self.dictionary = dictionary
              }
              var dictionary: [String: Any]
              // End expansion for "@DictionaryStorage"
          
          var name: String
          var height: Measurement<UnitLength>
          @DictionaryStorage(key: "birth_date") var birthDate: Date?
      }
    • 16:59 - The @DictionaryStorage conformance macro: macro definition

      /// Adds accessors to get and set the value of the specified property in a dictionary
      /// property called `storage`.
      @attached(conformance)
      @attached(member, names: named(dictionary), named(init(dictionary:)))
      @attached(memberAttribute)
      @attached(accessor)
      macro DictionaryStorage(key: String? = nil)
    • 17:09 - The @DictionaryStorage conformance macro: usage

      struct Person
              // Begin expansion for "@DictionaryStorage"
              : DictionaryRepresentable
              // End expansion for "@DictionaryStorage"
      {
          var name: String
          var height: Measurement<UnitLength>
          @DictionaryStorage(key: "birth_date") var birthDate: Date?
      }
    • 17:28 - @DictionaryStorage starting point

      struct Person: DictionaryRepresentable {
          init(dictionary: [String: Any]) { self.dictionary = dictionary }
          var dictionary: [String: Any]
      
          var name: String {
              get { dictionary["name"]! as! String }
              set { dictionary["name"] = newValue }
          }
          var height: Measurement<UnitLength> {
              get { dictionary["height"]! as! Measurement<UnitLength> }
              set { dictionary["height"] = newValue }
          }
          var birthDate: Date? {
              get { dictionary["birth_date"] as! Date? }
              set { dictionary["birth_date"] = newValue as Any? }
          }
      }
    • 17:32 - @DictionaryStorage ending point

      @DictionaryStorage
      struct Person
              // Begin expansion for "@DictionaryStorage"
              : DictionaryRepresentable
              // End expansion for "@DictionaryStorage"
      {
              // Begin expansion for "@DictionaryStorage"
              init(dictionary: [String: Any]) { self.dictionary = dictionary }
              var dictionary: [String: Any]
              // End expansion for "@DictionaryStorage"
      
              // Begin expansion for "@DictionaryStorage"
              @DictionaryStorage
              // End expansion for "@DictionaryStorage"
          var name: String
              // Begin expansion for "@DictionaryStorage"
              {
                  get { dictionary["name"]! as! String }
                  set { dictionary["name"] = newValue }
              }
              // End expansion for "@DictionaryStorage"
      
              // Begin expansion for "@DictionaryStorage"
              @DictionaryStorage
              // End expansion for "@DictionaryStorage"
          var height: Measurement<UnitLength>
              // Begin expansion for "@DictionaryStorage"
              {
                  get { dictionary["height"]! as! Measurement<UnitLength> }
                  set { dictionary["height"] = newValue }
              }
              // End expansion for "@DictionaryStorage"
      
          @DictionaryStorage(key: "birth_date")
          var birthDate: Date?
              // Begin expansion for "@DictionaryStorage"
              {
                  get { dictionary["birth_date"] as! Date? }
                  set { dictionary["birth_date"] = newValue as Any? }
              }
              // End expansion for "@DictionaryStorage"
      }
    • 17:35 - @DictionaryStorage ending point (without expansions)

      @DictionaryStorage
      struct Person {
          var name: String
          var height: Measurement<UnitLength>
      
          @DictionaryStorage(key: "birth_date")
          var birthDate: Date?
      }
    • 18:01 - Macro implementations

      /// Creates a tuple containing both the result of `expr` and its source code represented as a
      /// `String`.
      @freestanding(expression)
      macro stringify<T>(_ expr: T) -> (T, String) = #externalMacro(
                                                         module: "MyLibMacros",
                                                         type: "StringifyMacro"
                                                     )
    • 19:18 - Implementing @DictionaryStorage’s @attached(member) role (1)

      import SwiftSyntax
      import SwiftSyntaxMacros
      import SwiftSyntaxBuilder
      
      struct DictionaryStorageMacro: MemberMacro {
          static func expansion(
              of attribute: AttributeSyntax,
              providingMembersOf declaration: some DeclGroupSyntax,
              in context: some MacroExpansionContext
          ) throws -> [DeclSyntax] {
              return [
                 "init(dictionary: [String: Any]) { self.dictionary = dictionary }",
                 "var dictionary: [String: Any]"
              ]
          }
      }
    • 19:52 - Code used to demonstrate SwiftSyntax trees

      @DictionaryStorage
      struct Person {
          var name: String
          var height: Measurement<UnitLength>
          @DictionaryStorage(key: "birth_date")
          var birthDate: Date?
      }
    • 22:00 - Implementing @DictionaryStorage’s @attached(member) role (2)

      import SwiftSyntax
      import SwiftSyntaxMacros
      import SwiftSyntaxBuilder
      
      struct DictionaryStorageMacro: MemberMacro {
          static func expansion(
              of attribute: AttributeSyntax,
              providingMembersOf declaration: some DeclGroupSyntax,
              in context: some MacroExpansionContext
          ) throws -> [DeclSyntax] {
              return [
                 "init(dictionary: [String: Any]) { self.dictionary = dictionary }",
                 "var dictionary: [String: Any]"
              ]
          }
      }
    • 24:29 - A type that @DictionaryStorage isn’t compatible with

      @DictionaryStorage
      enum Gender {
          case other(String)
          case female
          case male
      
              // Begin expansion for "@DictionaryStorage"
              init(dictionary: [String: Any]) { self.dictionary = dictionary }
              var dictionary: [String: Any]
              // End expansion for "@DictionaryStorage"
      }
    • 25:17 - Expansion method with error checking

      import SwiftSyntax
      import SwiftSyntaxMacros
      import SwiftSyntaxBuilder
      
      struct DictionaryStorageMacro: MemberMacro {
          static func expansion(
              of attribute: AttributeSyntax,
              providingMembersOf declaration: some DeclGroupSyntax,
              in context: some MacroExpansionContext
          ) throws -> [DeclSyntax] {
              guard declaration.is(StructDeclSyntax.self) else {
                  let structError = Diagnostic(
                      node: attribute,
                      message: MyLibDiagnostic.notAStruct
                  )
                  context.diagnose(structError)
                  return []
              }
              return [
                  "init(dictionary: [String: Any]) { self.dictionary = dictionary }",
                  "var dictionary: [String: Any]"
              ]
          }
      }
      
      enum MyLibDiagnostic: String, DiagnosticMessage {
          case notAStruct
      
          var severity: DiagnosticSeverity { return .error }
      
          var message: String {
              switch self {
              case .notAStruct:
                  return "'@DictionaryStorage' can only be applied to a 'struct'"
              }
          }
      
          var diagnosticID: MessageID {
              MessageID(domain: "MyLibMacros", id: rawValue)
          }
      }
    • 29:32 - Parameter list for `ArrayND.makeIndex`

      FunctionParameterListSyntax {
          for dimension in 0 ..< numDimensions {
              FunctionParameterSyntax(
                  firstName: .wildcardToken(),
                  secondName: .identifier("i\(dimension)"),
                  type: TypeSyntax("Int")
              )
          }
      }
    • 30:17 - The #unwrap expression macro: revisited

      let image = #unwrap(downloadedImage, message: "was already checked")
      
                  // Begin expansion for "#unwrap"
                  { [downloadedImage] in
                      guard let downloadedImage else {
                          preconditionFailure(
                              "Unexpectedly found nil: ‘downloadedImage’ " + "was already checked",
                              file: "main/ImageLoader.swift",
                              line: 42
                          )
                      }
                      return downloadedImage
                  }()
                  // End expansion for "#unwrap"
    • 30:38 - Implementing the #unwrap expression macro: start

      static func makeGuardStmt() -> StmtSyntax {
          return """
              guard let downloadedImage else {
                  preconditionFailure(
                      "Unexpectedly found nil: ‘downloadedImage’ " + "was already checked",
                      file: "main/ImageLoader.swift",
                      line: 42
                  )
              }
          """
      }
    • 30:57 - Implementing the #unwrap expression macro: the message string

      static func makeGuardStmt(message: ExprSyntax) -> StmtSyntax {
          return """
              guard let downloadedImage else {
                  preconditionFailure(
                      "Unexpectedly found nil: ‘downloadedImage’ " + \(message),
                      file: "main/ImageLoader.swift",
                      line: 42
                  )
              }
          """
      }
    • 31:21 - Implementing the #unwrap expression macro: the variable name

      static func makeGuardStmt(wrapped: TokenSyntax, message: ExprSyntax) -> StmtSyntax {
          return """
              guard let \(wrapped) else {
                  preconditionFailure(
                      "Unexpectedly found nil: ‘downloadedImage’ " + \(message),
                      file: "main/ImageLoader.swift",
                      line: 42
                  )
              }
          """
      }
    • 31:44 - Implementing the #unwrap expression macro: interpolating a string as a literal

      static func makeGuardStmt(wrapped: TokenSyntax, message: ExprSyntax) -> StmtSyntax {
          let messagePrefix = "Unexpectedly found nil: ‘downloadedImage’ "
      
          return """
              guard let \(wrapped) else {
                  preconditionFailure(
                      \(literal: messagePrefix) + \(message),
                      file: "main/ImageLoader.swift",
                      line: 42
                  )
              }
          """
      }
    • 32:11 - Implementing the #unwrap expression macro: adding an expression as a string

      static func makeGuardStmt(wrapped: TokenSyntax,
                                 originalWrapped: ExprSyntax,
                                 message: ExprSyntax) -> StmtSyntax {
          let messagePrefix = "Unexpectedly found nil: ‘\(originalWrapped.description)’ "
      
          return """
              guard let \(wrapped) else {
                  preconditionFailure(
                      \(literal: messagePrefix) + \(message),
                      file: "main/ImageLoader.swift",
                      line: 42
                  )
              }
          """
      }
    • 33:00 - Implementing the #unwrap expression macro: inserting the file and line numbers

      static func makeGuardStmt(wrapped: TokenSyntax,
                                 originalWrapped: ExprSyntax,
                                 message: ExprSyntax,
                                 in context: some MacroExpansionContext) -> StmtSyntax {
          let messagePrefix = "Unexpectedly found nil: ‘\(originalWrapped.description)’ "
          let originalLoc = context.location(of: originalWrapped)!
      
          return """
              guard let \(wrapped) else {
                  preconditionFailure(
                      \(literal: messagePrefix) + \(message),
                      file: \(originalLoc.file),
                      line: \(originalLoc.line)
                  )
              }
          """
      }
    • 34:05 - The #unwrap expression macro, with a name conflict

      let wrappedValue = "🎁"
      let image = #unwrap(request.downloadedImage, message: "was \(wrappedValue)")
      
                  // Begin expansion for "#unwrap"
                  { [wrappedValue = request.downloadedImage] in
                      guard let wrappedValue else {
                          preconditionFailure(
                              "Unexpectedly found nil: ‘request.downloadedImage’ " +
                                  "was \(wrappedValue)",
                              file: "main/ImageLoader.swift",
                              line: 42
                          )
                      }
                      return wrappedValue
                  }()
                  // End expansion for "#unwrap"
    • 34:30 - The MacroExpansion.makeUniqueName() method

      let captureVar = context.makeUniqueName()
      
      return  """
              { [\(captureVar) = \(originalWrapped)] in
                  \(makeGuardStmt(wrapped: captureVar, …))
                  \(makeReturnStmt(wrapped: captureVar))
              }
              """
    • 35:44 - Declaring a macro’s names

      @attached(conformance)
      @attached(member, names: named(dictionary), named(init(dictionary:)))
      @attached(memberAttribute)
      @attached(accessor)
      macro DictionaryStorage(key: String? = nil)
      
      
      
      @attached(peer, names: overloaded)
      macro AddCompletionHandler(parameterName: String = "completionHandler")
      
      
      
      @freestanding(declaration, names: arbitrary)
      macro makeArrayND(n: Int)
    • 38:28 - Macros are testable

      import MyLibMacros
      import XCTest
      import SwiftSyntaxMacrosTestSupport
      
      final class MyLibTests: XCTestCase {
          func testMacro() {
              assertMacroExpansion(
                  """
                  @DictionaryStorage var name: String
                  """,
                  expandedSource: """
                  var name: String {
                      get { dictionary["name"]! as! String }
                      set { dictionary["name"] = newValue }
                  }
                  """,
                  macros: ["DictionaryStorage": DictionaryStorageMacro.self])
          }
      }

Developer Footer

  • Vidéos
  • WWDC23
  • Expand on Swift macros
  • Open Menu Close Menu
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    • App Store
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • Icon Composer
    • SF Symbols
    Open Menu Close Menu
    • Accessibility
    • Accessories
    • Apple Intelligence
    • Audio & Video
    • Augmented Reality
    • Business
    • Design
    • Distribution
    • Education
    • Games
    • Health & Fitness
    • In-App Purchase
    • Localization
    • Maps & Location
    • Machine Learning & AI
    • Security
    • Safari & Web
    Open Menu Close Menu
    • Documentation
    • Downloads
    • Sample Code
    • Videos
    Open Menu Close Menu
    • Help Guides & Articles
    • Contact Us
    • Forums
    • Feedback & Bug Reporting
    • System Status
    Open Menu Close Menu
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles
    • Feedback Assistant
    Open Menu Close Menu
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program
    • Mini Apps Partner Program
    • News Partner Program
    • Video Partner Program
    • Security Bounty Program
    • Security Research Device Program
    Open Menu Close Menu
    • Meet with Apple
    • Apple Developer Centers
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Academies
    • WWDC
    Read the latest news.
    Get the Apple Developer app.
    Copyright © 2026 Apple Inc. All rights reserved.
    Terms of Use Privacy Policy Agreements and Guidelines