Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 0 additions & 107 deletions Packages/StrandDesign/Sources/StrandDesign/SportIcon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -394,113 +394,6 @@ public enum WorkoutTypeIconography {
}
}

/// Preferred glyph identity ignoring OS availability — used to assert catalogue uniqueness.
public static func preferredIdentity(for type: KnownWorkoutType) -> String {
switch type {
case .running: return "system:figure.run"
case .walking: return "system:figure.walk"
case .nordicWalking: return "system:figure.walk.motion"
case .ballet: return "system:figure.barre"
case .billiards: return "system:8.circle.fill"
case .breakdancing: return "system:figure.socialdance"
case .cheerleading: return "system:megaphone.fill"
case .darts: return "system:target"
case .discGolf: return "system:flag.fill"
case .hurlingCamogie: return "system:sportscourt.fill"
case .jiuJitsu: return "system:figure.wrestling"
case .judo: return "system:figure.taichi"
case .kiteboarding: return "system:wind"
case .motocross: return "system:fuelpump.fill"
case .muayThai: return "system:hand.raised.fill"
case .paintball: return "system:scope"
case .parkour: return "system:figure.play"
case .polo: return "system:flag.2.crossed.fill"
case .skydiving: return "system:airplane.departure"
case .hiking: return "system:figure.hiking"
case .cycling: return "system:figure.outdoor.cycle"
case .openWaterSwim: return "system:figure.open.water.swim"
case .rowing: return "system:figure.outdoor.rowing"
case .treadmillRun: return "system:figure.run.treadmill"
case .treadmillWalk: return "system:figure.walk.treadmill"
case .indoorCycle: return "system:figure.indoor.cycle"
case .poolSwim: return "system:figure.pool.swim"
case .rowMachine: return "system:figure.indoor.rowing"
case .elliptical: return "system:figure.elliptical"
case .strength: return "system:dumbbell.fill"
case .bodybuilding: return "system:figure.strengthtraining.traditional"
case .weightlifting: return "system:figure.strengthtraining.functional"
case .hiit: return "system:figure.highintensity.intervaltraining"
case .yoga: return "system:figure.yoga"
case .pilates: return "system:figure.pilates"
case .boxing: return "system:figure.boxing"
case .basketball: return "system:figure.basketball"
case .soccer: return "system:figure.outdoor.soccer"
case .baseball: return "system:figure.baseball"
case .badminton: return "system:figure.badminton"
case .tennis: return "system:figure.tennis"
case .squash: return "system:figure.squash"
case .racquetball: return "system:figure.racquetball"
case .tableTennis: return "system:figure.table.tennis"
case .volleyball: return "system:figure.volleyball"
case .martialArts: return "system:figure.martial.arts"
case .dancing: return "system:figure.dance"
case .golf: return "system:figure.golf"
case .climbing: return "system:figure.climbing"
case .stretching: return "system:figure.flexibility"
case .skiing: return "system:figure.skiing.downhill"
case .snowboarding: return "system:figure.snowboarding"
case .padel: return "custom:padelRacket"
case .pickleball: return "system:figure.pickleball"
case .bowling: return "system:figure.bowling"
case .iceHockey: return "system:figure.hockey"
case .americanFootball: return "system:figure.american.football"
case .australianFootball: return "system:figure.australian.football"
case .rugby: return "system:figure.rugby"
case .cricket: return "system:figure.cricket"
case .softball: return "system:figure.softball"
case .handball: return "system:figure.handball"
case .waterPolo: return "system:figure.waterpolo"
case .frisbee: return "system:figure.disc.sports"
case .surfing: return "system:figure.surfing"
case .kayaking: return "system:oar.2.crossed"
case .sailing: return "system:figure.sailing"
case .scubaDiving: return "system:figure.water.fitness"
case .iceSkating: return "system:figure.ice.skating"
case .inlineSkating: return "system:figure.skating"
case .snowshoeing: return "system:figure.skiing.crosscountry"
case .gymnastics: return "system:figure.gymnastics"
case .fencing: return "system:figure.fencing"
case .calisthenics: return "system:figure.core.training"
case .stairClimber: return "system:figure.stair.stepper"
case .bootCamp: return "system:figure.step.training"
case .lacrosse: return "system:figure.lacrosse"
case .fieldHockey: return "system:figure.field.hockey"
case .crossfit: return "system:figure.cross.training"
case .kickboxing: return "system:figure.kickboxing"
case .mountainBiking: return "system:bicycle"
case .skateboarding: return "system:skateboard"
case .standUpPaddleboard: return "system:water.waves"
case .spinning: return "system:bicycle.circle"
case .jumpRope: return "system:figure.jumprope"
case .powerlifting: return "system:dumbbell"
case .rucking: return "system:backpack"
case .sandVolleyball: return "system:beach.umbrella"
case .archery: return "system:figure.archery"
case .fishing: return "system:figure.fishing"
case .hunting: return "system:figure.hunting"
case .curling: return "system:figure.curling"
case .netball: return "system:basketball"
case .gaelicFootball: return "system:soccerball"
case .spikeball: return "system:volleyball"
case .meditation: return "system:figure.mind.and.body"
case .horsebackRiding: return "system:figure.equestrian.sports"
case .wheelchair: return "system:figure.roll"
case .gaming: return "system:gamecontroller.fill"
case .motorRacing: return "system:steeringwheel"
case .other: return "system:figure.mixed.cardio"
}
}

/// Runtime glyph identity (after availability resolution).
public static func identity(for type: KnownWorkoutType) -> String {
switch glyph(for: type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,6 @@ final class WorkoutTypeIconTests: XCTestCase {
XCTAssertEqual(KnownWorkoutType.resolving("evening dancing class"), .dancing)
}

func testPreferredIdentitiesAreUnique() {
var seen = Set<String>()
for type in KnownWorkoutType.allCases {
let id = WorkoutTypeIconography.preferredIdentity(for: type)
XCTAssertFalse(seen.contains(id), "Duplicate preferred icon identity \(id) for \(type.rawValue)")
seen.insert(id)
}
XCTAssertEqual(seen.count, KnownWorkoutType.allCases.count)
}

func testRuntimeIdentitiesAreUnique() {
var seen = Set<String>()
for type in KnownWorkoutType.allCases {
Expand Down Expand Up @@ -67,6 +57,20 @@ final class WorkoutTypeIconTests: XCTestCase {
.custom(.padelRacket))
}

func testSportSymbolBridgeUsesRuntimeSystemGlyphs() {
for type in KnownWorkoutType.allCases {
if case .system(let name) = WorkoutTypeIconography.glyph(for: type) {
XCTAssertEqual(sportSymbol(type.rawValue), name, type.rawValue)
}
}
}

func testSportSymbolBridgeHandlesCustomAndUnknownWorkouts() {
XCTAssertEqual(sportSymbol("Padel"), "figure.tennis")
XCTAssertEqual(sportSymbol("unrecognised workout"), "figure.mixed.cardio")
XCTAssertEqual(sportSymbol(""), "figure.mixed.cardio")
}

func testSportSymbolBridgeNonEmpty() {
for type in KnownWorkoutType.allCases {
XCTAssertFalse(sportSymbol(type.rawValue).isEmpty)
Expand Down
4 changes: 2 additions & 2 deletions StrandTests/WorkoutCatalogTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ final class WorkoutCatalogTests: XCTestCase {
}

/// Iconography lockstep: every catalogue sport must resolve to a `KnownWorkoutType` with a unique
/// preferred glyph so the live-workout Liquid Glass control never shares icons across types.
/// runtime glyph on the current OS, matching the live-workout Liquid Glass control.
func testCatalogueSportsHaveUniqueWorkoutTypeIcons() {
let catalogNames = WorkoutCatalog.all.map(\.name)
let knownNames = KnownWorkoutType.allCases.map(\.rawValue)
Expand All @@ -103,7 +103,7 @@ final class WorkoutCatalogTests: XCTestCase {
guard let type = KnownWorkoutType.exact(matching: sport.name) else {
return XCTFail("No KnownWorkoutType for catalogue sport \(sport.name)")
}
let id = WorkoutTypeIconography.preferredIdentity(for: type)
let id = WorkoutTypeIconography.identity(for: type)
XCTAssertFalse(identities.contains(id), "Duplicate icon \(id) for \(sport.name)")
identities.insert(id)
}
Expand Down
Loading