3.4 — SF Symbols
Opening scenario
You need a “heart” icon for a like button. The designer sends a custom SVG. You import it, scale it, recolor it for light/dark, and ship. Two weeks later, the designer wants the heart to animate when tapped. You write a custom shape interpolation. Two weeks later, the designer wants the heart at the user’s accessibility text size. You write font-scaling math. Two weeks later, iOS 19 adds a beautiful new heart-fill animation built into the system… that you can’t use, because you committed to custom SVG.
You should have used SF Symbols from day one. This chapter shows you why and how.
| Aspect | Detail |
|---|---|
| Tool | SF Symbols app — free, Mac only |
| Library | ~6,900 symbols in SF Symbols 6 (2024) |
| Format | Apple proprietary; rendered as a font |
| Custom symbols | SVG export from Figma → import to SF Symbols app |
| Animation | symbolEffect modifier in SwiftUI |
Concept → Why → How → Code
What SF Symbols actually is
SF Symbols is a typeface of icons. Each symbol is a glyph, sized and weighted to match SF Pro (Apple’s system font). When you render Image(systemName: "heart.fill"), you’re not loading an asset — you’re rendering text from a font file shipped with iOS.
Implications:
- Symbols scale with
.font()modifier exactly like text - Symbols inherit
foregroundStyleexactly like text - Symbols pair perfectly with
.body,.headline, etc. — they sit on the same baseline as adjacent text - Symbols cost zero asset weight (no PNGs in your bundle)
- Symbols update with iOS — when iOS adds a new variant, your app gets it for free
Browsing and naming
Download the SF Symbols app from Apple. The app browser shows every symbol with its canonical name (heart, heart.fill, heart.circle, heart.circle.fill, heart.text.square).
The naming convention:
[concept].[variant].[shape].[fill/lines]
Examples:
heart→ outlineheart.fill→ filledheart.circle→ outline heart inside outline circleheart.circle.fill→ filled circle backgroundxmark.bin.fill→ trash with X (compound concept)
When you can’t remember a name, open the SF Symbols app and search by keyword — “delete,” “user,” “send.”
Symbol variants
The same symbol comes in multiple style families. SwiftUI exposes these via .symbolVariant():
// Outline (default)
Image(systemName: "heart")
// Filled
Image(systemName: "heart").symbolVariant(.fill)
// or just
Image(systemName: "heart.fill")
// Slash (e.g. "muted")
Image(systemName: "speaker.slash")
// Circle
Image(systemName: "person.circle")
In a Label, variants propagate automatically — useful for tab bars where all icons should be filled when selected:
TabView {
Tab("Home", systemImage: "house") { HomeView() }
Tab("Profile", systemImage: "person") { ProfileView() }
}
.symbolVariant(.fill) // all tab icons become filled
Rendering modes — the four colorings
let icon = Image(systemName: "cloud.sun.rain.fill")
// 1. Monochrome — single tint
icon.symbolRenderingMode(.monochrome).foregroundStyle(.blue)
// 2. Hierarchical — single tint, opacity layers (drops 100/50/25%)
icon.symbolRenderingMode(.hierarchical).foregroundStyle(.blue)
// 3. Palette — multiple distinct colors
icon.symbolRenderingMode(.palette)
.foregroundStyle(.gray, .yellow, .blue) // cloud, sun, rain
// 4. Multicolor — Apple's preset multicolor
icon.symbolRenderingMode(.multicolor)
Use hierarchical as the default for system UI — it’s the most legible across light/dark. Use palette when you need brand colors on a multipart symbol. Use multicolor sparingly for visual emphasis.
Symbol effects (iOS 17+)
Animations baked into the symbol itself:
@State private var liked = false
Image(systemName: liked ? "heart.fill" : "heart")
.symbolEffect(.bounce, value: liked)
.foregroundStyle(liked ? .red : .secondary)
.onTapGesture { liked.toggle() }
The catalogue of effects (iOS 17+, expanded in 18):
| Effect | What it does |
|---|---|
.bounce | One-time scale-up bounce |
.pulse | Continuous opacity pulse |
.variableColor | Per-layer color animation (great for activity indicators) |
.scale | Scale up/down on state change |
.appear / .disappear | Animated mount/unmount |
.replace | Smooth morph from one symbol to another (iOS 17+) |
.wiggle | Subtle wiggle (iOS 18+) |
.breathe | Slow breathing animation (iOS 18+) |
.rotate | Rotation (iOS 18+) |
// Replace effect: smoothly morph between symbols
Image(systemName: liked ? "heart.fill" : "heart")
.contentTransition(.symbolEffect(.replace.byLayer))
These look professional, hit 60fps, and ship in a single line. Don’t roll your own.
Sizing and weight
Symbols inherit font sizing:
Image(systemName: "star.fill")
.font(.title2) // sets size
.fontWeight(.bold) // sets weight
// or together:
Image(systemName: "star.fill")
.imageScale(.large)
.symbolRenderingMode(.hierarchical)
imageScale (.small, .medium, .large) is a relative scale on top of the current font size. Combined with Dynamic Type, your icons scale automatically when the user bumps text size in Settings.
Custom SF Symbols
When you genuinely need a custom icon (brand mark, domain-specific glyph), draw it in Figma, export as SVG, then import into the SF Symbols app:
- Open SF Symbols app
- File → Export… any existing symbol as a template (e.g.
heart.svg) - Open the SVG in Figma; replace the path with your custom glyph (keep the canvas frame, baseline guides)
- Export back as SVG
- SF Symbols app → File → Open → your SVG → adjust by weight/scale → File → Export as
.svg(Symbol) - Drop the
.svginto Xcode’s Asset Catalog (Symbol Asset, not Image Asset) - Reference:
Image(systemName: "my.custom.symbol")— yes, even custom symbols usesystemNameif dropped in Asset Catalog as a symbol
Custom symbols inherit all the rendering modes and effects. They are first-class citizens.
Accessibility
SF Symbols are automatically labeled by VoiceOver based on their name (“heart, filled”). Override when needed:
Image(systemName: "heart.fill")
.accessibilityLabel("Add to favorites")
For decorative-only icons (e.g. inline with a labeled button), mark them:
Button {
save()
} label: {
Label("Save", systemImage: "tray.and.arrow.down")
}
// → Label handles accessibility: VoiceOver reads "Save, button"
// → The image is decorative
In the wild
- Apple Mail is SF Symbols top to bottom — sidebar icons, toolbar buttons, swipe action icons. Open it as the reference implementation.
- Apple Health mixes SF Symbols (system actions) with custom symbol assets (specific organs, vitals) — all using
Image(systemName:). - Cash App uses custom SF Symbols for its dollar-sign branding alongside system symbols in nav.
- Notion for iOS uses SF Symbols across almost every UI surface — their migration from custom PNGs to SF Symbols reportedly saved ~3 MB of app size.
Common misconceptions
- “SF Symbols are too generic — they make my app look like every other app.” That’s the point of system controls. Use SF Symbols for system-task icons (back, share, settings) and reserve custom illustration for hero/marketing surfaces.
- “SF Symbols can’t be colored.” They absolutely can — see palette and multicolor rendering modes.
- “I need to ship PDF/PNG fallbacks for older iOS.” SF Symbols ship back to iOS 13. If you’re targeting iOS 15+, you can use any symbol introduced in SF Symbols 4 (2022). Filter the SF Symbols app by deployment target.
- “Custom SF Symbols are too much work.” A one-time 30-minute setup per custom glyph. After that, free animations, free Dark Mode, free Dynamic Type. Cheaper than rolling your own image system.
- “Symbol effects are eye candy I should turn off for performance.” They’re GPU-accelerated and cheap. Use them — they’re a free quality signal.
Seasoned engineer’s take
The single biggest delta between an app that feels Apple-native and one that feels third-party is how the icons are handled. Use SF Symbols. Use the rendering modes. Use the symbol effects. The reviewer who would have given you 4 stars gives 5 because the heart bounces correctly.
When a designer hands you a custom icon for a system task (back, settings, share, send), push back: “Is there a reason we’re not using the system SF Symbol for this?” Often the answer is “I forgot SF Symbols had one” — and you save yourself an export pipeline.
Keep an eye on each year’s WWDC for new SF Symbols. Apple adds 500+ per release. Symbols you wanted three years ago may now exist.
TIP: SF Symbols app → Sample tab — lets you preview symbols with custom text size, weight, color, and rendering mode side by side. Use it before committing to a symbol.
WARNING: Don’t use SF Symbols outside Apple platforms (no Android, no web) unless you own a license — Apple’s terms restrict use. For cross-platform brand symbols, draw custom.
Interview corner
Junior-level: “What is SF Symbols?”
Apple’s icon system — a glyph font shipping with every Apple OS, accessible via Image(systemName:). Around 6,900 symbols, free, animatable, scale with Dynamic Type.
Mid-level: “What’s the difference between hierarchical and palette rendering modes?”
Hierarchical uses one tint at varying opacity to imply layers (single color brand-friendly). Palette uses multiple distinct foregroundStyles, one per layer of the symbol — useful when symbol parts represent different semantic meanings (cloud=gray, sun=yellow).
Senior-level: “Your designer wants a custom logo as an icon. Walk me through your pipeline.”
Draw in Figma at SF Symbols template grid sizes. Export as SVG. Import to SF Symbols app, align baselines and weight axes. Export as .svg symbol asset. Drop into Xcode Asset Catalog as Symbol. Use via Image(systemName:). Custom symbols inherit .symbolRenderingMode, Dynamic Type, and color modifiers — write once, ship to every accessibility setting.
Red flag in candidates: Reaching for PNG sprite sheets or custom font rendering for icons in 2025. Means they haven’t kept up with the platform.
Lab preview
You’ll consume SF Symbols extensively in Lab 3.1 — Figma to SwiftUI and audit incorrect icon usage in Lab 3.2.