# Different macro implementation for different platforms

**URL:** <https://forums.swift.org/t/different-macro-implementation-for-different-platforms/67693>\
**Category:** Using Swift\
**Tags:** macros\
**Created:** [October 5, 2023, 6:21pm UTC](https://forums.swift.org/t/different-macro-implementation-for-different-platforms/67693 "2023-10-05T18:21:57Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![mattcox](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mattcox/32/10946_2.png) [@mattcox](https://forums.swift.org/u/mattcox)\
**Post date:** [October 5, 2023, 6:21pm UTC](https://forums.swift.org/t/different-macro-implementation-for-different-platforms/67693/1 "2023-10-05T18:21:57Z")

</div>

I am trying to create a macro that allows me to reference a color by name in the asset bundle.

For example, being able to do something like this:

```swift
let myColor = #color("MyColor")

```

On macOS, I would want this to expand to:

```swift
"NSColor(named: \(argument)) ?? NSColor.clear"

```

On iOS, I would want it to expand to:

```swift
"UIColor(named: \(argument))"

```

I have written the following macro:

```swift
public struct ColorMacro: ExpressionMacro {
	public static func expansion(of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext) throws -> ExprSyntax {
		guard let argument = node.argumentList.first?.expression else {
			throw MacroError.invalidArguments
		}

#if canImport(AppKit)
		return "NSColor(named: \(argument)) ?? NSColor.clear"
#elseif canImport(UIKit)
		return "UIColor(named: \(argument))"
#else
		#error("Unsupported platform")
#endif
	}
}

```

And...

```swift
#if canImport(AppKit)
import AppKit

@freestanding(expression)
public macro color(_ named: String) -> NSColor = #externalMacro(module: "SwatchbookMacrosMacros", type: "ColorMacro")
#elseif canImport(UIKit)
import UIKit

@freestanding(expression)
public macro color(_ named: String) -> UIColor = #externalMacro(module: "SwatchbookMacrosMacros", type: "ColorMacro")
#endif

```

This works fine when building for macOS, but when using in a target that is being built for iOS, it seems to still be trying to use the AppKit branch and referencing `NSColor`.

Am I doing something wrong? Is it using the platform that is being built on to determine availability, rather than the target platform?

---

<div class="post-metadata">

**Author:** ![mattcox](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mattcox/32/10946_2.png) [@mattcox](https://forums.swift.org/u/mattcox)\
**Post date:** [October 5, 2023, 6:25pm UTC](https://forums.swift.org/t/different-macro-implementation-for-different-platforms/67693/2 "2023-10-05T18:25:31Z")

</div>

This also doesn't work:

```swift
public struct ColorMacro: ExpressionMacro {
	public static func expansion(of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext) throws -> ExprSyntax {
		guard let argument = node.argumentList.first?.expression else {
			throw MacroError.invalidArguments
		}

		return """
		#if canImport(AppKit)
				NSColor(named: \(argument)) ?? NSColor.clear
		#elseif canImport(UIKit)
				UIColor(named: \(argument))
		#else
				#error("Unsupported platform")
		#endif
		"""
	}
}

```

`Error: Expected macro expansion to produce an expression`

---

<div class="post-metadata">

**Author:** ![allevato](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/allevato/32/385_2.png) [@allevato](https://forums.swift.org/u/allevato)\
**Post date:** [October 5, 2023, 6:30pm UTC](https://forums.swift.org/t/different-macro-implementation-for-different-platforms/67693/3 "2023-10-05T18:30:29Z")

</div>

The problem here is that an `#if` block is an `IfConfigDeclSyntax`, rather than an expression. You could wrap the whole thing in an immediately executed closure to make it into an expression though:

```swift
return """
  {
    #if canImport(AppKit)
      NSColor(named: \(argument)) ?? NSColor.clear
    #elseif canImport(UIKit)
      UIColor(named: \(argument))
    #else
      #error("Unsupported platform")
    #endif
  }()
  """

```

But if you're going to do all that, it might just be cleaner and simpler to create a helper function in your macro's library target and call that:

```swift
func __colorHelper(_ name: String) {
  #if canImport(AppKit)
    NSColor(named: name) ?? NSColor.clear
  #elseif canImport(UIKit)
    UIColor(named: name)
  #else
    #error("Unsupported platform")
  #endif
}

```

And have your macro implementation call that function:

```swift
return """
  YourModuleName.__colorHelper(\(argument))
  """

```

---

<div class="post-metadata">

**Author:** ![mattcox](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mattcox/32/10946_2.png) [@mattcox](https://forums.swift.org/u/mattcox)\
**Post date:** [October 5, 2023, 6:35pm UTC](https://forums.swift.org/t/different-macro-implementation-for-different-platforms/67693/4 "2023-10-05T18:35:17Z")

</div>

Ah I see! So the macro itself is platform agnostic, but the macro library target can declare the helper code targeting the specific platform that's being used, to actually do the work. Ok, that makes more sense.

Still discovering macros, and there seems to be many moving parts that are a challenge to get right.

Thanks.
