Nucleus
OS integration

SF Symbols

Type-safe Kotlin constants for Apple SF Symbol names used by macOS APIs.

sf-symbols provides Apple's SF Symbols names as typed Kotlin constants. macOS APIs that draw a symbol take its name as a string; instead of writing "scissors" by hand, you reference SFSymbolObjectsAndTools.SCISSORS and let the compiler check it. Other Nucleus macOS modules accept these constants wherever they take an icon.

Add the dependency

build.gradle.kts
dependencies {
    implementation("dev.nucleusframework:nucleus.sf-symbols:2.0.5")
}

menu-macos exposes sf-symbols through api(). If you already depend on menu-macos, the constants are on your classpath without a separate declaration.

Reference a symbol

Every symbol is a value of the sealed interface SFSymbol. Pick a constant from the category enum that matches the symbol's context:

import dev.nucleusframework.sfsymbols.SFSymbolGeneral
import dev.nucleusframework.sfsymbols.SFSymbolMedia
import dev.nucleusframework.sfsymbols.SFSymbolObjectsAndTools

val gear = SFSymbolGeneral.GEAR
val play = SFSymbolMedia.PLAY
val scissors = SFSymbolObjectsAndTools.SCISSORS

Each constant carries its SF Symbols name in symbolName. Pass the constant straight to a macOS API that takes a symbol — for example, a native menu item icon:

import dev.nucleusframework.menu.macos.NsMenuItemImage
import dev.nucleusframework.sfsymbols.SFSymbolObjectsAndTools

val icon = NsMenuItemImage.SystemSymbol(SFSymbolObjectsAndTools.SCISSORS)

For a name not covered by the constants, or to target a symbol from a newer SF Symbols release, use SFSymbol.Custom:

import dev.nucleusframework.sfsymbols.SFSymbol

val custom = SFSymbol.Custom("my.custom.symbol")

How it works

Every constant exposes a symbolName string that holds its SF Symbols name — GEAR resolves to "gear", PLAY to "play". Custom wraps an arbitrary string in the same type, so any SFSymbol is ultimately a name that macOS can look up with NSImage.imageWithSystemSymbolName.

When you hand an SFSymbol to a macOS module, only the symbolName reaches the native layer. The system resolves that name to the installed SF Symbols glyph at render time, so the symbol follows the user's system appearance without shipping any image assets.

The constants are plain strings, so the module compiles on any platform. SF Symbols themselves require macOS 11 or later; on earlier macOS or other operating systems, a name resolves to nothing.

API reference

Categories

SFSymbol is a sealed interface. Each category is an enum whose entries map to SF Symbols names; Custom is a value class for everything else. All 21 category enums implement SFSymbol, so any constant is usable wherever an SFSymbol is expected.

CategorySymbolsExamples
SFSymbolGeneral1362GEAR, GEARSHAPE
SFSymbolHuman596ACCESSIBILITY, BRAIN
SFSymbolShapes542APP, APP_BADGE
SFSymbolArrows535ARROW_LEFT, ARROW_CLOCKWISE
SFSymbolTransportation417AIRPLANE, AIRPLANE_ARRIVAL
SFSymbolObjectsAndTools358SCISSORS, FOLDER, TRASH
SFSymbolDevices349DESKTOPCOMPUTER, KEYBOARD
SFSymbolNumbers301_0_CIRCLE, _0_CIRCLE_FILL
SFSymbolWeather286CLOUD, SUN_MAX, BOLT
SFSymbolStatus273CHECKMARK, INFO_CIRCLE
SFSymbolCommerce264BAG, BAG_BADGE_PLUS
SFSymbolMedia238PLAY, PAUSE, STOP
SFSymbolLetters142A_CIRCLE, A_SQUARE
SFSymbolTextFormatting117BOLD, CHARACTER
SFSymbolCommunication101ENVELOPE, PHONE, MESSAGE
SFSymbolPower76POWER, POWER_CIRCLE
SFSymbolSecurity62KEY, KEY_FILL
SFSymbolHealth57ALLERGENS, BASEBALL
SFSymbolLocation47FLAG, COMPASS_DRAWING
SFSymbolTime45ALARM, ALARM_FILL
SFSymbolConnectivity27CABLE_COAXIAL, CABLE_CONNECTOR

Number symbols begin with a digit, which is not a valid Kotlin identifier, so their constants are prefixed with an underscore: SFSymbolNumbers._0_CIRCLE maps to "0.circle".

Build a custom symbol

SFSymbol.Custom(symbolName: String) is an inline value class. Its symbolName is used verbatim, so use it for symbols outside the predefined constants or from a newer SF Symbols version:

SFSymbol.Custom("bird.fill")
SFSymbol.Custom("my.custom.symbol")

Use a symbol with menu-macos

NsMenuItemImage.SystemSymbol has an overload that takes an SFSymbol directly, so a constant becomes a menu item icon without touching its symbolName:

import dev.nucleusframework.menu.macos.NsMenuItemImage
import dev.nucleusframework.sfsymbols.SFSymbolGeneral

NsMenuItemImage.SystemSymbol(SFSymbolGeneral.GEAR)

What's next