chore: commit all pending changes

This commit is contained in:
Trey t
2026-02-10 18:15:36 -06:00
parent b993ed3613
commit 53cc532ca9
51 changed files with 2583 additions and 268 deletions

View File

@@ -10,7 +10,13 @@ import CloudKit
// MARK: - Record Type Constants
enum CKRecordType {
private extension String {
nonisolated var ckTrimmed: String {
trimmingCharacters(in: .whitespacesAndNewlines)
}
}
nonisolated enum CKRecordType {
static let team = "Team"
static let stadium = "Stadium"
static let game = "Game"
@@ -25,7 +31,7 @@ enum CKRecordType {
// MARK: - CKTeam
struct CKTeam {
nonisolated struct CKTeam {
static let idKey = "teamId"
static let canonicalIdKey = "canonicalId"
static let nameKey = "name"
@@ -62,12 +68,22 @@ struct CKTeam {
/// The canonical ID string from CloudKit (e.g., "team_nba_atl")
var canonicalId: String? {
record[CKTeam.canonicalIdKey] as? String
if let explicit = (record[CKTeam.canonicalIdKey] as? String)?.ckTrimmed, !explicit.isEmpty {
return explicit
}
let fallback = record.recordID.recordName.ckTrimmed
return fallback.isEmpty ? nil : fallback
}
/// The stadium canonical ID string from CloudKit (e.g., "stadium_nba_state_farm_arena")
var stadiumCanonicalId: String? {
record[CKTeam.stadiumCanonicalIdKey] as? String
if let explicit = (record[CKTeam.stadiumCanonicalIdKey] as? String)?.ckTrimmed, !explicit.isEmpty {
return explicit
}
if let stadiumRef = record[CKTeam.stadiumRefKey] as? CKRecord.Reference {
return stadiumRef.recordID.recordName.ckTrimmed
}
return nil
}
/// The conference canonical ID string from CloudKit (e.g., "nba_eastern")
@@ -82,16 +98,18 @@ struct CKTeam {
var team: Team? {
// Use teamId field, or fall back to record name
let id = (record[CKTeam.idKey] as? String) ?? record.recordID.recordName
let id = ((record[CKTeam.idKey] as? String)?.ckTrimmed) ?? record.recordID.recordName.ckTrimmed
let abbreviation = (record[CKTeam.abbreviationKey] as? String)?.ckTrimmed
let sportRaw = (record[CKTeam.sportKey] as? String)?.ckTrimmed
let city = (record[CKTeam.cityKey] as? String)?.ckTrimmed
guard !id.isEmpty,
let abbreviation = record[CKTeam.abbreviationKey] as? String,
let sportRaw = record[CKTeam.sportKey] as? String,
let sport = Sport(rawValue: sportRaw),
let city = record[CKTeam.cityKey] as? String
let abbreviation, !abbreviation.isEmpty,
let sportRaw, let sport = Sport(rawValue: sportRaw.uppercased()),
let city, !city.isEmpty
else { return nil }
// Name defaults to abbreviation if not provided
let name = record[CKTeam.nameKey] as? String ?? abbreviation
let name = ((record[CKTeam.nameKey] as? String)?.ckTrimmed).flatMap { $0.isEmpty ? nil : $0 } ?? abbreviation
// Stadium reference is optional - use placeholder string if not present
let stadiumId: String
@@ -122,7 +140,7 @@ struct CKTeam {
// MARK: - CKStadium
struct CKStadium {
nonisolated struct CKStadium {
static let idKey = "stadiumId"
static let canonicalIdKey = "canonicalId"
static let nameKey = "name"
@@ -157,25 +175,31 @@ struct CKStadium {
/// The canonical ID string from CloudKit (e.g., "stadium_nba_state_farm_arena")
var canonicalId: String? {
record[CKStadium.canonicalIdKey] as? String
if let explicit = (record[CKStadium.canonicalIdKey] as? String)?.ckTrimmed, !explicit.isEmpty {
return explicit
}
let fallback = record.recordID.recordName.ckTrimmed
return fallback.isEmpty ? nil : fallback
}
var stadium: Stadium? {
// Use stadiumId field, or fall back to record name
let id = (record[CKStadium.idKey] as? String) ?? record.recordID.recordName
let id = ((record[CKStadium.idKey] as? String)?.ckTrimmed) ?? record.recordID.recordName.ckTrimmed
let name = (record[CKStadium.nameKey] as? String)?.ckTrimmed
let city = (record[CKStadium.cityKey] as? String)?.ckTrimmed
guard !id.isEmpty,
let name = record[CKStadium.nameKey] as? String,
let city = record[CKStadium.cityKey] as? String
let name, !name.isEmpty,
let city, !city.isEmpty
else { return nil }
// These fields are optional in CloudKit
let state = record[CKStadium.stateKey] as? String ?? ""
let state = ((record[CKStadium.stateKey] as? String)?.ckTrimmed) ?? ""
let location = record[CKStadium.locationKey] as? CLLocation
let capacity = record[CKStadium.capacityKey] as? Int ?? 0
let imageURL = (record[CKStadium.imageURLKey] as? String).flatMap { URL(string: $0) }
let sportRaw = record[CKStadium.sportKey] as? String ?? "MLB"
let sport = Sport(rawValue: sportRaw) ?? .mlb
let timezoneIdentifier = record[CKStadium.timezoneIdentifierKey] as? String
let sportRaw = ((record[CKStadium.sportKey] as? String)?.ckTrimmed).flatMap { $0.isEmpty ? nil : $0 } ?? "MLB"
let sport = Sport(rawValue: sportRaw.uppercased()) ?? .mlb
let timezoneIdentifier = (record[CKStadium.timezoneIdentifierKey] as? String)?.ckTrimmed
return Stadium(
id: id,
@@ -195,7 +219,7 @@ struct CKStadium {
// MARK: - CKGame
struct CKGame {
nonisolated struct CKGame {
static let idKey = "gameId"
static let canonicalIdKey = "canonicalId"
static let homeTeamRefKey = "homeTeamRef"
@@ -232,31 +256,64 @@ struct CKGame {
/// The canonical ID string from CloudKit (e.g., "game_nba_202526_20251021_hou_okc")
var canonicalId: String? {
record[CKGame.canonicalIdKey] as? String
if let explicit = (record[CKGame.canonicalIdKey] as? String)?.ckTrimmed, !explicit.isEmpty {
return explicit
}
let fallback = record.recordID.recordName.ckTrimmed
return fallback.isEmpty ? nil : fallback
}
/// The home team canonical ID string from CloudKit (e.g., "team_nba_okc")
var homeTeamCanonicalId: String? {
record[CKGame.homeTeamCanonicalIdKey] as? String
if let explicit = (record[CKGame.homeTeamCanonicalIdKey] as? String)?.ckTrimmed, !explicit.isEmpty {
return explicit
}
if let homeRef = record[CKGame.homeTeamRefKey] as? CKRecord.Reference {
return homeRef.recordID.recordName.ckTrimmed
}
return nil
}
/// The away team canonical ID string from CloudKit (e.g., "team_nba_hou")
var awayTeamCanonicalId: String? {
record[CKGame.awayTeamCanonicalIdKey] as? String
if let explicit = (record[CKGame.awayTeamCanonicalIdKey] as? String)?.ckTrimmed, !explicit.isEmpty {
return explicit
}
if let awayRef = record[CKGame.awayTeamRefKey] as? CKRecord.Reference {
return awayRef.recordID.recordName.ckTrimmed
}
return nil
}
/// The stadium canonical ID string from CloudKit (e.g., "stadium_nba_paycom_center")
var stadiumCanonicalId: String? {
record[CKGame.stadiumCanonicalIdKey] as? String
if let explicit = (record[CKGame.stadiumCanonicalIdKey] as? String)?.ckTrimmed, !explicit.isEmpty {
return explicit
}
if let stadiumRef = record[CKGame.stadiumRefKey] as? CKRecord.Reference {
return stadiumRef.recordID.recordName.ckTrimmed
}
return nil
}
func game(homeTeamId: String, awayTeamId: String, stadiumId: String) -> Game? {
let id = (record[CKGame.idKey] as? String) ?? record.recordID.recordName
let id = ((record[CKGame.idKey] as? String)?.ckTrimmed) ?? record.recordID.recordName.ckTrimmed
let sportRaw = (record[CKGame.sportKey] as? String)?.ckTrimmed
let season: String
if let seasonString = (record[CKGame.seasonKey] as? String)?.ckTrimmed, !seasonString.isEmpty {
season = seasonString
} else if let seasonInt = record[CKGame.seasonKey] as? Int {
season = String(seasonInt)
} else if let seasonInt64 = record[CKGame.seasonKey] as? Int64 {
season = String(seasonInt64)
} else if let seasonNumber = record[CKGame.seasonKey] as? NSNumber {
season = seasonNumber.stringValue
} else {
return nil
}
guard !id.isEmpty,
let dateTime = record[CKGame.dateTimeKey] as? Date,
let sportRaw = record[CKGame.sportKey] as? String,
let sport = Sport(rawValue: sportRaw),
let season = record[CKGame.seasonKey] as? String
let sportRaw, let sport = Sport(rawValue: sportRaw.uppercased())
else { return nil }
return Game(
@@ -267,7 +324,13 @@ struct CKGame {
dateTime: dateTime,
sport: sport,
season: season,
isPlayoff: (record[CKGame.isPlayoffKey] as? Int) == 1,
isPlayoff: {
if let value = record[CKGame.isPlayoffKey] as? Int { return value == 1 }
if let value = record[CKGame.isPlayoffKey] as? Int64 { return value == 1 }
if let value = record[CKGame.isPlayoffKey] as? NSNumber { return value.intValue == 1 }
if let value = record[CKGame.isPlayoffKey] as? Bool { return value }
return false
}(),
broadcastInfo: record[CKGame.broadcastInfoKey] as? String
)
}
@@ -275,7 +338,7 @@ struct CKGame {
// MARK: - CKLeagueStructure
struct CKLeagueStructure {
nonisolated struct CKLeagueStructure {
static let idKey = "structureId"
static let sportKey = "sport"
static let typeKey = "type"
@@ -308,15 +371,18 @@ struct CKLeagueStructure {
/// Convert to SwiftData model for local storage
func toModel() -> LeagueStructureModel? {
guard let id = record[CKLeagueStructure.idKey] as? String,
let sport = record[CKLeagueStructure.sportKey] as? String,
let id = ((record[CKLeagueStructure.idKey] as? String)?.ckTrimmed) ?? record.recordID.recordName.ckTrimmed
let sport = (record[CKLeagueStructure.sportKey] as? String)?.ckTrimmed.uppercased()
guard !id.isEmpty,
let sport, !sport.isEmpty,
let typeRaw = record[CKLeagueStructure.typeKey] as? String,
let structureType = LeagueStructureType(rawValue: typeRaw),
let name = record[CKLeagueStructure.nameKey] as? String
let structureType = LeagueStructureType(rawValue: typeRaw.lowercased()),
let name = (record[CKLeagueStructure.nameKey] as? String)?.ckTrimmed,
!name.isEmpty
else { return nil }
let abbreviation = record[CKLeagueStructure.abbreviationKey] as? String
let parentId = record[CKLeagueStructure.parentIdKey] as? String
let abbreviation = (record[CKLeagueStructure.abbreviationKey] as? String)?.ckTrimmed
let parentId = (record[CKLeagueStructure.parentIdKey] as? String)?.ckTrimmed
let displayOrder = record[CKLeagueStructure.displayOrderKey] as? Int ?? 0
let schemaVersion = record[CKLeagueStructure.schemaVersionKey] as? Int ?? SchemaVersion.current
let lastModified = record[CKLeagueStructure.lastModifiedKey] as? Date ?? record.modificationDate ?? Date()
@@ -337,7 +403,7 @@ struct CKLeagueStructure {
// MARK: - CKSport
struct CKSport {
nonisolated struct CKSport {
static let idKey = "sportId"
static let abbreviationKey = "abbreviation"
static let displayNameKey = "displayName"
@@ -357,16 +423,49 @@ struct CKSport {
/// Convert to CanonicalSport for local storage
func toCanonical() -> CanonicalSport? {
guard let id = record[CKSport.idKey] as? String,
let abbreviation = record[CKSport.abbreviationKey] as? String,
let displayName = record[CKSport.displayNameKey] as? String,
let iconName = record[CKSport.iconNameKey] as? String,
let colorHex = record[CKSport.colorHexKey] as? String,
let seasonStartMonth = record[CKSport.seasonStartMonthKey] as? Int,
let seasonEndMonth = record[CKSport.seasonEndMonthKey] as? Int
let id = ((record[CKSport.idKey] as? String)?.ckTrimmed) ?? record.recordID.recordName.ckTrimmed
let seasonStartMonth: Int?
if let intValue = record[CKSport.seasonStartMonthKey] as? Int {
seasonStartMonth = intValue
} else if let int64Value = record[CKSport.seasonStartMonthKey] as? Int64 {
seasonStartMonth = Int(int64Value)
} else if let numberValue = record[CKSport.seasonStartMonthKey] as? NSNumber {
seasonStartMonth = numberValue.intValue
} else {
seasonStartMonth = nil
}
let seasonEndMonth: Int?
if let intValue = record[CKSport.seasonEndMonthKey] as? Int {
seasonEndMonth = intValue
} else if let int64Value = record[CKSport.seasonEndMonthKey] as? Int64 {
seasonEndMonth = Int(int64Value)
} else if let numberValue = record[CKSport.seasonEndMonthKey] as? NSNumber {
seasonEndMonth = numberValue.intValue
} else {
seasonEndMonth = nil
}
guard !id.isEmpty,
let abbreviation = (record[CKSport.abbreviationKey] as? String)?.ckTrimmed,
!abbreviation.isEmpty,
let displayName = (record[CKSport.displayNameKey] as? String)?.ckTrimmed,
!displayName.isEmpty,
let iconName = (record[CKSport.iconNameKey] as? String)?.ckTrimmed,
!iconName.isEmpty,
let colorHex = (record[CKSport.colorHexKey] as? String)?.ckTrimmed,
!colorHex.isEmpty,
let seasonStartMonth,
let seasonEndMonth
else { return nil }
let isActive = (record[CKSport.isActiveKey] as? Int ?? 1) == 1
let isActive: Bool = {
if let value = record[CKSport.isActiveKey] as? Int { return value == 1 }
if let value = record[CKSport.isActiveKey] as? Int64 { return value == 1 }
if let value = record[CKSport.isActiveKey] as? NSNumber { return value.intValue == 1 }
if let value = record[CKSport.isActiveKey] as? Bool { return value }
return true
}()
let schemaVersion = record[CKSport.schemaVersionKey] as? Int ?? SchemaVersion.current
let lastModified = record[CKSport.lastModifiedKey] as? Date ?? record.modificationDate ?? Date()
@@ -388,7 +487,7 @@ struct CKSport {
// MARK: - CKStadiumAlias
struct CKStadiumAlias {
nonisolated struct CKStadiumAlias {
static let aliasNameKey = "aliasName"
static let stadiumCanonicalIdKey = "stadiumCanonicalId"
static let validFromKey = "validFrom"
@@ -415,8 +514,10 @@ struct CKStadiumAlias {
/// Convert to SwiftData model for local storage
func toModel() -> StadiumAlias? {
guard let aliasName = record[CKStadiumAlias.aliasNameKey] as? String,
let stadiumCanonicalId = record[CKStadiumAlias.stadiumCanonicalIdKey] as? String
let aliasName = ((record[CKStadiumAlias.aliasNameKey] as? String)?.ckTrimmed) ?? record.recordID.recordName.ckTrimmed
guard !aliasName.isEmpty,
let stadiumCanonicalId = (record[CKStadiumAlias.stadiumCanonicalIdKey] as? String)?.ckTrimmed,
!stadiumCanonicalId.isEmpty
else { return nil }
let validFrom = record[CKStadiumAlias.validFromKey] as? Date
@@ -437,7 +538,7 @@ struct CKStadiumAlias {
// MARK: - CKTeamAlias
struct CKTeamAlias {
nonisolated struct CKTeamAlias {
static let idKey = "aliasId"
static let teamCanonicalIdKey = "teamCanonicalId"
static let aliasTypeKey = "aliasType"
@@ -468,11 +569,14 @@ struct CKTeamAlias {
/// Convert to SwiftData model for local storage
func toModel() -> TeamAlias? {
guard let id = record[CKTeamAlias.idKey] as? String,
let teamCanonicalId = record[CKTeamAlias.teamCanonicalIdKey] as? String,
let aliasTypeRaw = record[CKTeamAlias.aliasTypeKey] as? String,
let aliasType = TeamAliasType(rawValue: aliasTypeRaw),
let aliasValue = record[CKTeamAlias.aliasValueKey] as? String
let id = ((record[CKTeamAlias.idKey] as? String)?.ckTrimmed) ?? record.recordID.recordName.ckTrimmed
guard !id.isEmpty,
let teamCanonicalId = (record[CKTeamAlias.teamCanonicalIdKey] as? String)?.ckTrimmed,
!teamCanonicalId.isEmpty,
let aliasTypeRaw = (record[CKTeamAlias.aliasTypeKey] as? String)?.ckTrimmed,
let aliasType = TeamAliasType(rawValue: aliasTypeRaw.lowercased()),
let aliasValue = (record[CKTeamAlias.aliasValueKey] as? String)?.ckTrimmed,
!aliasValue.isEmpty
else { return nil }
let validFrom = record[CKTeamAlias.validFromKey] as? Date
@@ -495,7 +599,7 @@ struct CKTeamAlias {
// MARK: - CKTripPoll
struct CKTripPoll {
nonisolated struct CKTripPoll {
static let pollIdKey = "pollId"
static let titleKey = "title"
static let ownerIdKey = "ownerId"
@@ -594,7 +698,7 @@ struct CKTripPoll {
// MARK: - CKPollVote
struct CKPollVote {
nonisolated struct CKPollVote {
static let voteIdKey = "voteId"
static let pollIdKey = "pollId"
static let voterIdKey = "voterId"
@@ -640,4 +744,3 @@ struct CKPollVote {
)
}
}