lukepistrol / SFSymbolsMacro

A Swift Macro for "type-safe" SF Symbols.
MIT License
183 stars 2 forks source link
swift swift-macros swift-package swift-package-manager swift5-9 swiftui xcode15

GitHub Twitter: @lukeeep_

SFSymbolsMacro

This Swift Macro provides an easy way to make the use of SF Symbols in Swift more or less "type-safe".

Installation

Xcode

  1. Click File > Add Package Dependencies
  2. Paste the following link into the search field on the upper-right:
    https://github.com/lukepistrol/SFSymbolsMacro.git

Swift Package Manager

In Package.swift:

dependencies: [
    .package(url: "https://github.com/lukepistrol/SFSymbolsMacro.git", from: "0.1.0")
]

And then add the dependency to your targets.

Usage

Simply create an enum which will hold all the SF Symbols for your project:

enum Symbols: String {
    case circle
    case circleFill = "circle.fill"
    case shareIcon = "square.and.arrow.up"
    case globe
}

Then simply import SFSymbolsMacro and add the @SFSymbol macro annotation to the enum:

import SFSymbolsMacro
import SwiftUI

@SFSymbol
enum Symbols: String { ... }

The macro will then validate each case and the expanded macro will look something like this:

enum Symbols: String {
    case circle
    case circleFill = "circle.fill"
    case shareIcon = "square.and.arrow.up"
    case globe

    var image: Image {
        Image(systemName: self.rawValue)
    }
    var name: String {
        self.rawValue
    }
    #if canImport(UIKit)
    func uiImage(configuration: UIImage.Configuration? = nil) -> UIImage {
        UIImage(systemName: self.rawValue, withConfiguration: configuration)!
    }
    #else
    func nsImage(accessibilityDescription: String? = nil) -> NSImage {
        return NSImage(systemSymbolName: self.rawValue, accessibilityDescription: accessibilityDescription)!
    }
    #endif
    func callAsFunction() -> String {
        return self.rawValue
    }
}

In your code you can then call a symbol:

var body: some View {
    VStack {
        Symbols.circleFill.image
        Label("Globe", systemImage: Symbols.globe.name)
        // the above can also be written as
        Label("Globe", systemImage: Symbols.globe())
    }
}

In case the provided raw value is not a valid SF Symbol, Xcode will show a compile error at the enum-case in question:

explicit raw value

inferred raw value

Contribution

If you have any ideas on how to take this further I'm happy to discuss things in an issue.


Buy Me A Coffee