feat(apple): type-safe SF Symbols via SFSafeSymbols

Replaces every stringly-typed SF Symbol name with a compile-time-checked
SFSymbol case, mirroring the L10n accessor approach. A mistyped or
OS-unavailable symbol is now a build error instead of a silently blank
glyph at runtime.

Adds the SFSafeSymbols SPM package (project.yml) and migrates all call
sites: Image(systemName:)/Label(systemImage:) -> systemSymbol, and the
five symbol-carrying view properties (AppDestination.systemSymbol,
SettingsRow.icon, AboutPoint.symbol, MethodRow.icon, PolicyOption.icon)
flipped from String to SFSymbol end to end.
This commit is contained in:
2026-07-23 17:40:07 +02:00
parent ef42875ddb
commit 1563bf80d6
12 changed files with 74 additions and 59 deletions

View File

@@ -1,4 +1,5 @@
import SwiftUI
import SFSafeSymbols
/// Settings screen, rebuilt on a native `Form` with `NavigationStack` push
/// navigation. The model stays the source of truth via a derived path binding.
@@ -25,21 +26,21 @@ struct SettingsScreen: View {
Form {
Section {
NavigationLink(value: SettingsSection.preferences) {
SettingsRow(icon: "person.crop.circle", title: String(localized: L10n.Preferences.title), value: model.state.username)
SettingsRow(icon: .personCropCircle, title: String(localized: L10n.Preferences.title), value: model.state.username)
}
NavigationLink(value: SettingsSection.appearance) {
SettingsRow(icon: "sun.max", title: String(localized: L10n.Appearance.title), value: themeModeLabel(model.state.themeMode))
SettingsRow(icon: .sunMax, title: String(localized: L10n.Appearance.title), value: themeModeLabel(model.state.themeMode))
}
}
Section {
NavigationLink(value: SettingsSection.notifications) {
SettingsRow(icon: "bell", title: String(localized: L10n.Notifications.title), value: nil)
SettingsRow(icon: .bell, title: String(localized: L10n.Notifications.title), value: nil)
}
NavigationLink(value: SettingsSection.storage) {
SettingsRow(icon: "internaldrive", title: String(localized: L10n.Storage.title), value: nil)
SettingsRow(icon: .internaldrive, title: String(localized: L10n.Storage.title), value: nil)
}
NavigationLink(value: SettingsSection.about) {
SettingsRow(icon: "info.circle", title: String(localized: L10n.About.title), value: nil)
SettingsRow(icon: .infoCircle, title: String(localized: L10n.About.title), value: nil)
}
}
}
@@ -69,7 +70,7 @@ struct SettingsScreen: View {
Button {
showBugReport = true
} label: {
Label(String(localized: L10n.About.bugReport), systemImage: "ladybug")
Label(String(localized: L10n.About.bugReport), systemSymbol: .ladybug)
}
}
}
@@ -110,13 +111,13 @@ private struct SettingsSectionContent: View {
}
struct SettingsRow: View {
let icon: String
let icon: SFSymbol
let title: String
let value: String?
var body: some View {
HStack(spacing: 12) {
Image(systemName: icon)
Image(systemSymbol: icon)
.foregroundStyle(.tint)
.frame(width: 26)
Text(title).foregroundStyle(.primary)