Skip to content

SF Symbols-like API for third-party icon libraries. Use Image(icon: "ph.house") to access Phosphor Icons and Ionicons with on-demand packaging via SPM Build Tool Plugin.

License

Notifications You must be signed in to change notification settings

CyonCode/SFSymbolsProvider

Repository files navigation

SFSymbolsProvider

Swift 5.9+ Platforms License: MIT

A Swift library that provides an SF Symbols-like API for third-party icon libraries. Use familiar SwiftUI patterns like Image(icon: "ph.house") to access Phosphor Icons and Ionicons with on-demand packaging via SPM Build Tool Plugin.

Features

  • Zero Configuration - Icons are bundled with the package; just add the dependency and start using icons
  • SF Symbols-like API - Use Image(icon: "ph.house") just like Image(systemName: "house")
  • On-demand packaging - Only icons used in your code are bundled into your app
  • SPM Build Tool Plugin - Automatic source scanning and asset generation at build time
  • Template rendering - Full support for SwiftUI color modifiers like .foregroundStyle()
  • Multiple icon libraries - Phosphor Icons (5 weights) and Ionicons (3 variants)
  • All Apple platforms - iOS, macOS, watchOS, tvOS, and visionOS

Requirements

  • Swift 5.9+
  • iOS 15+ / macOS 12+ / watchOS 8+ / tvOS 15+ / visionOS 1+
  • Xcode 15+

Installation

Swift Package Manager

Add SFSymbolsProvider to your Package.swift:

dependencies: [
    .package(url: "https://github.com/CyonCode/SFSymbolsProvider.git", from: "1.0.0")
]

Then add it to your target with the plugin:

.executableTarget(
    name: "YourApp",
    dependencies: ["SFSymbolsProvider"],
    plugins: [
        .plugin(name: "SFSymbolsProviderPlugin", package: "SFSymbolsProvider")
    ]
)

For Xcode Projects (iOS/watchOS/tvOS)

Due to a limitation in how Xcode handles SPM build tool plugin outputs, iOS builds require a one-time setup:

  1. Add the package via File > Add Package Dependencies... and select SFSymbolsProvider library only
  2. In Build Settings, search for "User Script Sandboxing" and set it to No
  3. In your target's Build Phases, add a new Run Script Phase (after "Compile Sources")
  4. Add the following script:
"${BUILD_DIR}/../../SourcePackages/checkouts/SFSymbolsProvider/Scripts/ios-build-icons.sh"
  1. Configure when the script runs (choose one):
    • Option A: Uncheck "Based on dependency analysis" — script runs every build (recommended for development)
    • Option B: Add Output File $(DERIVED_FILE_DIR)/SFSymbolsProviderIcons.bundle — faster incremental builds, but requires Clean Build when icon usage changes

Note: macOS targets built with swift build work without this extra step. The first build compiles the icon generation tool (adds ~5-10s).

Usage

Basic Usage

import SwiftUI
import SFSymbolsProvider

struct ContentView: View {
    var body: some View {
        VStack {
            // Phosphor Icons
            Image(icon: "ph.house")           // Regular weight
            Image(icon: "ph.house.fill")      // Fill weight
            Image(icon: "ph.gear.bold")       // Bold weight
            
            // Ionicons
            Image(icon: "ion.home")           // Default variant
            Image(icon: "ion.home.outline")   // Outline variant
            Image(icon: "ion.settings.sharp") // Sharp variant
        }
    }
}

Styling with SwiftUI Modifiers

Icons use template rendering, so standard SwiftUI modifiers work seamlessly:

Image(icon: "ph.heart.fill")
    .foregroundStyle(.red)
    .font(.system(size: 24))

Image(icon: "ion.star")
    .foregroundStyle(.yellow)
    .frame(width: 32, height: 32)

Handling Missing Icons

The initializer returns nil for invalid icon names, so use optional binding or provide a fallback:

// Optional binding
if let icon = Image(icon: "ph.house") {
    icon
}

// With fallback
Image(icon: "ph.house") ?? Image(systemName: "house")

API Reference

Image Extension

public extension Image {
    /// Creates an image from a Phosphor or Ionicons icon name.
    /// - Parameters:
    ///   - iconName: The icon name in format "ph.name[.weight]" or "ion.name[.variant]"
    ///   - bundle: The bundle containing the generated assets (default: .main)
    /// - Returns: An Image if the icon name is valid, nil otherwise
    init?(icon iconName: String, bundle: Bundle = .main)
}

Icon Name Format

Provider Format Examples
Phosphor ph.{name} ph.house, ph.gear, ph.user
Phosphor ph.{name}.{weight} ph.house.fill, ph.gear.bold, ph.user.thin
Ionicons ion.{name} ion.home, ion.settings, ion.person
Ionicons ion.{name}.{variant} ion.home.outline, ion.settings.sharp

Supported Weights (Phosphor)

Weight Description
regular Default weight (no suffix needed)
thin Thinnest stroke
light Light stroke
bold Bold stroke
fill Filled/solid icons

Supported Variants (Ionicons)

Variant Description
default Default style (no suffix needed)
outline Outline/stroke style
sharp Sharp corners variant

How It Works

  1. Build Time Scanning: The SPM Build Tool Plugin scans your Swift source files for Image(icon: "...") calls
  2. Icon Detection: Regex-based detection identifies all Phosphor (ph.*) and Ionicons (ion.*) references
  3. Asset Generation: Only the icons you use are copied to a generated .xcassets catalog
  4. Template Rendering: Icons are configured with template-rendering-intent for color customization

Advanced Configuration

By default, SFSymbolsProvider uses bundled icons. To use custom icon paths (e.g., for newer icon versions), create a sfsymbols.json file in your package/project root:

{
    "phosphorPath": "/path/to/phosphor-icons",
    "ioniconsPath": "/path/to/ionicons.designerpack"
}

Known Limitations

  • Xcode iOS builds require setup - SPM plugin outputs aren't automatically bundled for iOS. See Xcode Projects setup above.
  • No Duotone support - Phosphor Duotone icons require special two-color rendering (planned for v2.0)
  • String literals only - Dynamic icon names like Image(icon: variable) are not detected at build time
  • SwiftUI only - No UIKit/AppKit API provided
  • No Symbol Effects - iOS 17+ symbol animations are not supported

FAQ

Icons not showing on iOS Simulator/Device?

  1. Verify the Run Script Phase is after "Compile Sources" in Build Phases
  2. Ensure "Based on dependency analysis" is unchecked - the script must run every build
  3. Clean and rebuild - Product > Clean Build Folder (⇧⌘K), then rebuild
  4. Check the bundle exists - Look for SFSymbolsProviderIcons.bundle in your app's .app folder

First build takes a long time?

The first build compiles the icon generation tool from source. Subsequent builds reuse the cached tool and only take a few seconds.

Script fails with "swift build" errors?

The script uses env -i to isolate the Swift build from Xcode's environment. If you still have issues, ensure you have Swift command line tools installed: xcode-select --install

How do I verify the script is working?

Check the Build Log (⌘8) for "com.apple.actool.compilation-results" which shows the path to the generated Assets.car file.

Example App

See the Example/ directory for a complete iOS app demonstrating all features:

cd Example
swift build

License

SFSymbolsProvider is available under the MIT license. See the LICENSE file for more info.

Acknowledgments

About

SF Symbols-like API for third-party icon libraries. Use Image(icon: "ph.house") to access Phosphor Icons and Ionicons with on-demand packaging via SPM Build Tool Plugin.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •