Implemented complete TCA architecture for iOS and watchOS targets: - Authentication flow (Sign in with Apple + Yahoo OAuth) - OAuth token management with iCloud Key-Value Storage - Yahoo Fantasy Sports API client with async/await - Watch Connectivity for iPhone ↔ Watch data sync - Complete UI for both iPhone and Watch platforms Core features: - Matchup score display - Category breakdown with win/loss/tie indicators - Roster status tracking - Manual refresh functionality - Persistent data caching on Watch Technical stack: - The Composable Architecture for state management - Swift Concurrency (async/await, actors) - WatchConnectivity framework - Sign in with Apple - OAuth 2.0 authentication flow 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
85 lines
2.6 KiB
Swift
85 lines
2.6 KiB
Swift
//
|
|
// CategoryBreakdownView.swift
|
|
// FantasyWatch Watch App
|
|
//
|
|
// Created by Claude Code
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct CategoryBreakdownView: View {
|
|
let categories: [CategoryScore]
|
|
|
|
var body: some View {
|
|
List(categories) { category in
|
|
HStack(spacing: 8) {
|
|
Text(category.name)
|
|
.font(.caption)
|
|
.fontWeight(.semibold)
|
|
.frame(width: 40, alignment: .leading)
|
|
|
|
Spacer()
|
|
|
|
VStack(alignment: .trailing, spacing: 2) {
|
|
Text(category.userValue)
|
|
.font(.caption)
|
|
.fontWeight(.semibold)
|
|
.foregroundColor(colorForComparison(category.comparison))
|
|
|
|
Text(category.opponentValue)
|
|
.font(.caption2)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
|
|
comparisonIcon(category.comparison)
|
|
.font(.caption2)
|
|
.frame(width: 20)
|
|
}
|
|
}
|
|
.navigationTitle("Categories")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
|
|
private func colorForComparison(_ comparison: CategoryScore.ComparisonResult) -> Color {
|
|
switch comparison {
|
|
case .winning:
|
|
return .green
|
|
case .losing:
|
|
return .red
|
|
case .tied:
|
|
return .orange
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func comparisonIcon(_ comparison: CategoryScore.ComparisonResult) -> some View {
|
|
switch comparison {
|
|
case .winning:
|
|
Image(systemName: "arrow.up")
|
|
.foregroundColor(.green)
|
|
case .losing:
|
|
Image(systemName: "arrow.down")
|
|
.foregroundColor(.red)
|
|
case .tied:
|
|
Image(systemName: "equal")
|
|
.foregroundColor(.orange)
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
NavigationStack {
|
|
CategoryBreakdownView(
|
|
categories: [
|
|
CategoryScore(statID: "1", name: "G", userValue: "10", opponentValue: "8"),
|
|
CategoryScore(statID: "2", name: "A", userValue: "15", opponentValue: "18"),
|
|
CategoryScore(statID: "3", name: "PPP", userValue: "5", opponentValue: "5"),
|
|
CategoryScore(statID: "4", name: "SOG", userValue: "200", opponentValue: "195"),
|
|
CategoryScore(statID: "5", name: "W", userValue: "3", opponentValue: "4"),
|
|
CategoryScore(statID: "6", name: "GAA", userValue: "2.45", opponentValue: "2.80"),
|
|
CategoryScore(statID: "7", name: "SV%", userValue: "0.915", opponentValue: "0.905")
|
|
]
|
|
)
|
|
}
|
|
}
|