Xcode 27: compila sin warnings y migra API deprecadas de SwiftUI

- Imports de CoreData que faltaban (MemberImportVisibility) en AccountsView,
  ImportDataView, ChartsContainerView y AddSourceView.
- Aislamiento MainActor: IsolatedDefaultValues en el target de la app,
  LinearGradient @MainActor, WatchSyncMessage nonisolated, closures de
  ReviewPromptService/MonthlyCheckInStore/AppIntents.
- Warnings de valores sin usar (SnapshotGapDetector, DashboardLayoutStore,
  OnboardingView, GoalEditorView, CoreDataStack, SettingsViewModel).
- foregroundColor→foregroundStyle, cornerRadius→clipShape,
  navigationBarLeading/Trailing→topBarLeading/Trailing,
  Task.sleep(nanoseconds:)→sleep(for:), NavigationLink(isActive:)→
  navigationDestination(isPresented:).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F1u4K16xy7eQVtgsYNZ9Vn
This commit is contained in:
alexandrev-tibco
2026-09-16 11:44:49 +02:00
parent 464fbb2bad
commit a8a9ad64f3
39 changed files with 420 additions and 414 deletions
@@ -17,7 +17,7 @@ struct CategoryBreakdownCard: View {
} label: {
Text("See All")
.font(.subheadline)
.foregroundColor(.appPrimary)
.foregroundStyle(Color.appPrimary)
}
}
@@ -27,7 +27,7 @@ struct CategoryBreakdownCard: View {
}
.padding()
.background(Color(.systemBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
}
}
@@ -55,7 +55,7 @@ struct CategoryRowView: View {
Image(systemName: category.icon)
.font(.system(size: 14))
.foregroundColor(Color(hex: category.colorHex) ?? .gray)
.foregroundStyle(Color(hex: category.colorHex) ?? .gray)
}
// Name and percentage
@@ -65,12 +65,12 @@ struct CategoryRowView: View {
Text(category.formattedPercentage)
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
if let target = targetPercentage {
Text("Target \(String(format: "%.0f%%", target)) | Drift \(driftText ?? "")")
.font(.caption2)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
}
@@ -84,10 +84,10 @@ struct CategoryRowView: View {
HStack(spacing: 4) {
Text("CAGR")
.font(.caption2)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
Text(category.metrics.formattedCAGR)
.font(.caption.weight(.semibold))
.foregroundColor(category.metrics.cagr >= 0 ? .positiveGreen : .negativeRed)
.foregroundStyle(category.metrics.cagr >= 0 ? Color.positiveGreen : Color.negativeRed)
}
}
}
@@ -122,7 +122,7 @@ struct CategoryProgressBar: View {
Text(category.formattedPercentage)
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
GeometryReader { geometry in
@@ -130,12 +130,12 @@ struct CategoryProgressBar: View {
Rectangle()
.fill(Color.gray.opacity(0.1))
.frame(height: 6)
.cornerRadius(3)
.clipShape(RoundedRectangle(cornerRadius: 3))
Rectangle()
.fill(Color(hex: category.colorHex) ?? .gray)
.frame(width: geometry.size.width * progress, height: 6)
.cornerRadius(3)
.clipShape(RoundedRectangle(cornerRadius: 3))
}
}
.frame(height: 6)
@@ -166,7 +166,7 @@ struct SimpleCategoryList: View {
Text("(\(category.formattedPercentage))")
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
}
}
@@ -13,27 +13,27 @@ struct DataGapsBanner: View {
HStack(spacing: 12) {
Image(systemName: "exclamationmark.triangle.fill")
.font(.title3)
.foregroundColor(.orange)
.foregroundStyle(.orange)
VStack(alignment: .leading, spacing: 2) {
Text(String(format: String(localized: "gaps_banner_title"), totalMissingMonths))
.font(.subheadline.weight(.semibold))
.foregroundColor(.primary)
.foregroundStyle(.primary)
.multilineTextAlignment(.leading)
Text("Fill in missing data")
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
Spacer()
Image(systemName: "chevron.right")
.font(.caption.weight(.semibold))
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
.padding(14)
.background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
.overlay(
RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius, style: .continuous)
.stroke(Color.orange.opacity(0.35), lineWidth: 1)
@@ -44,7 +44,7 @@ struct DataGapsBanner: View {
Button(action: onDismiss) {
Image(systemName: "xmark.circle.fill")
.font(.body)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
.padding(6)
}
.buttonStyle(.plain)
@@ -89,7 +89,7 @@ struct DataGapsSheet: View {
.navigationTitle("Missing Data")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
ToolbarItem(placement: .topBarTrailing) {
Button("Done") { dismiss() }
}
}
@@ -109,7 +109,7 @@ struct DataGapsSheet: View {
Spacer()
Text(String(format: String(localized: "gaps_missing_count"), gap.missingMonths))
.font(.caption.weight(.semibold))
.foregroundColor(.orange)
.foregroundStyle(.orange)
.padding(.horizontal, 8)
.padding(.vertical, 3)
.background(Color.orange.opacity(0.12))
@@ -118,7 +118,7 @@ struct DataGapsSheet: View {
Text(rangeLabel(gap))
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
if let source = sourceProvider(gap.sourceId),
let firstMissing = gap.missingMonthDates.first {
@@ -147,7 +147,7 @@ struct EvolutionChartCard: View {
}
.padding()
.background(Color(.systemBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
.contentShape(Rectangle())
// simultaneousGesture so the swipe is recognized even over the chart,
@@ -206,16 +206,16 @@ struct EvolutionChartCard: View {
.frame(width: 72, height: 72)
Image(systemName: page.chartsType.icon)
.font(.system(size: 28))
.foregroundColor(.appPrimary)
.foregroundStyle(Color.appPrimary)
Image(systemName: "lock.circle.fill")
.font(.system(size: 22))
.foregroundColor(.appWarning)
.foregroundStyle(Color.appWarning)
.background(Circle().fill(Color(.systemBackground)))
.offset(x: 26, y: 24)
}
Text(String(localized: "home_chart_locked_sub"))
.font(.subheadline)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)
Button {
FirebaseService.shared.logPaywallShown(trigger: "home_charts")
@@ -226,7 +226,7 @@ struct EvolutionChartCard: View {
.padding(.horizontal, 20)
.padding(.vertical, 10)
.background(Color.appPrimary)
.foregroundColor(.white)
.foregroundStyle(.white)
.clipShape(Capsule())
}
}
@@ -246,7 +246,7 @@ struct EvolutionChartCard: View {
showGoalLines.toggle()
} label: {
Image(systemName: showGoalLines ? "target" : "slash.circle")
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
.accessibilityLabel(showGoalLines ? "Hide goals" : "Show goals")
@@ -256,7 +256,7 @@ struct EvolutionChartCard: View {
.font(.subheadline.weight(.semibold))
Text(selected.date.monthYearString)
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
}
}
@@ -303,7 +303,7 @@ struct EvolutionChartCard: View {
Text(item.category).font(.caption)
Spacer()
Text(String(format: "%.1f%%", pct)).font(.caption.weight(.medium))
Text(item.value.compactCurrencyString).font(.caption).foregroundColor(.secondary)
Text(item.value.compactCurrencyString).font(.caption).foregroundStyle(.secondary)
}
}
}
@@ -325,7 +325,7 @@ struct EvolutionChartCard: View {
} else {
Text("Not enough data to display chart")
.font(.subheadline)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
.frame(height: 200)
.frame(maxWidth: .infinity)
}
@@ -449,7 +449,7 @@ struct EvolutionChartCard: View {
.annotation(position: .topTrailing) {
Text(goal.name)
.font(.caption2)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
}
}
@@ -126,7 +126,7 @@ struct GuidedCheckInView: View {
if let category = source.category?.name {
Text(category)
.font(.subheadline)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
}
@@ -134,10 +134,10 @@ struct GuidedCheckInView: View {
VStack(spacing: 2) {
Text(String(localized: "guided_previous_value"))
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
Text(source.latestValue.currencyString)
.font(.title3.weight(.semibold))
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
}
@@ -150,7 +150,7 @@ struct GuidedCheckInView: View {
.padding(.vertical, 12)
.padding(.horizontal, 24)
.background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
.padding(.horizontal, 32)
liveDelta(for: source)
@@ -202,7 +202,7 @@ struct GuidedCheckInView: View {
} label: {
Text(String(localized: "guided_skip"))
.font(.subheadline.weight(.medium))
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
.accessibilityIdentifier("guided_skip_btn")
}
@@ -224,7 +224,7 @@ struct GuidedCheckInView: View {
Text("\(delta.currencyString) (\(String(format: "%+.1f%%", pct)))")
.font(.subheadline.weight(.semibold))
}
.foregroundColor(positive ? .positiveGreen : .negativeRed)
.foregroundStyle(positive ? Color.positiveGreen : Color.negativeRed)
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background((positive ? Color.positiveGreen : Color.negativeRed).opacity(0.12))
@@ -247,7 +247,7 @@ struct GuidedCheckInView: View {
.font(.title2.weight(.bold))
Text(String(localized: "guided_reflection_subtitle"))
.font(.subheadline)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
HStack(spacing: 10) {
@@ -272,8 +272,8 @@ struct GuidedCheckInView: View {
? Color.appPrimary.opacity(0.15)
: Color(.secondarySystemGroupedBackground)
)
.foregroundColor(mood == candidate ? .appPrimary : .primary)
.cornerRadius(12)
.foregroundStyle(mood == candidate ? Color.appPrimary : .primary)
.clipShape(RoundedRectangle(cornerRadius: 12))
.overlay(
RoundedRectangle(cornerRadius: 12)
.strokeBorder(mood == candidate ? Color.appPrimary : .clear, lineWidth: 1.5)
@@ -290,7 +290,7 @@ struct GuidedCheckInView: View {
.focused($noteFocused)
.padding(14)
.background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
.padding(.horizontal, 24)
Spacer()
@@ -319,7 +319,7 @@ struct GuidedCheckInView: View {
Image(systemName: "checkmark.seal.fill")
.font(.system(size: 64))
.foregroundColor(.positiveGreen)
.foregroundStyle(Color.positiveGreen)
.symbolEffect(.bounce, value: step)
Text(String(format: String(localized: "guided_done_title"), monthLabel))
@@ -335,7 +335,7 @@ struct GuidedCheckInView: View {
let pct = NSDecimalNumber(decimal: delta / totals.previous).doubleValue * 100
Text("\(delta.currencyString) (\(String(format: "%+.1f%%", pct)))")
.font(.subheadline.weight(.semibold))
.foregroundColor(delta >= 0 ? .positiveGreen : .negativeRed)
.foregroundStyle(delta >= 0 ? Color.positiveGreen : Color.negativeRed)
}
}
}
@@ -347,7 +347,7 @@ struct GuidedCheckInView: View {
Text(String(format: String(localized: "guided_streak"), streak))
}
.font(.subheadline.weight(.semibold))
.foregroundColor(.orange)
.foregroundStyle(.orange)
.padding(.horizontal, 14)
.padding(.vertical, 8)
.background(Color.orange.opacity(0.12))
@@ -21,7 +21,7 @@ struct InsightsRow: View {
HStack(spacing: 12) {
Image(systemName: insight.systemImage)
.font(.subheadline.weight(.semibold))
.foregroundColor(insight.accentColor)
.foregroundStyle(insight.accentColor)
.frame(width: 30, height: 30)
.background(insight.accentColor.opacity(0.12))
.clipShape(Circle())
@@ -29,15 +29,15 @@ struct InsightsRow: View {
VStack(alignment: .leading, spacing: 1) {
Text(insight.title)
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
Text(insight.value)
.font(.subheadline.weight(.semibold))
.foregroundColor(.primary)
.foregroundStyle(.primary)
.lineLimit(2)
if let detail = insight.detail {
Text(detail)
.font(.caption2)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
.lineLimit(1)
}
}
@@ -56,7 +56,7 @@ struct InsightsRow: View {
}
.padding(14)
.background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
}
.buttonStyle(.plain)
.id(insight.id)
@@ -116,7 +116,7 @@ struct MonthlyCheckInView: View {
} label: {
Image(systemName: "chevron.left")
.font(.subheadline.weight(.semibold))
.foregroundColor(.appPrimary)
.foregroundStyle(Color.appPrimary)
}
Text(monthLabel)
@@ -127,13 +127,13 @@ struct MonthlyCheckInView: View {
} label: {
Image(systemName: "chevron.right")
.font(.subheadline.weight(.semibold))
.foregroundColor(canGoToNextMonth ? .appPrimary : .secondary.opacity(0.3))
.foregroundStyle(canGoToNextMonth ? Color.appPrimary : .secondary.opacity(0.3))
}
.disabled(!canGoToNextMonth)
}
}
ToolbarItem(placement: .navigationBarTrailing) {
ToolbarItem(placement: .topBarTrailing) {
Button {
shareMonthlyCheckIn()
} label: {
@@ -230,11 +230,11 @@ struct MonthlyCheckInView: View {
)
)
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
} else {
Text("Start your first check-in anytime.")
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
}
}
@@ -242,10 +242,10 @@ struct MonthlyCheckInView: View {
if let completed = MonthlyCheckInStore.completionDate(for: referenceDate) {
HStack(spacing: 6) {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.positiveGreen)
.foregroundStyle(Color.positiveGreen)
Text("Completed \(completed.friendlyDescription)")
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
}
@@ -272,12 +272,12 @@ struct MonthlyCheckInView: View {
.frame(maxWidth: .infinity)
.padding(.vertical, 10)
.background(isCompleted ? Color.appSecondary.opacity(0.1) : Color.appPrimary.opacity(0.1))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
}
}
.padding()
.background(Color(.systemBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
}
@@ -310,7 +310,7 @@ struct MonthlyCheckInView: View {
Spacer()
Text("Optional")
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
VStack(alignment: .leading, spacing: 8) {
@@ -326,7 +326,7 @@ struct MonthlyCheckInView: View {
} label: {
Image(systemName: value <= starRating ? "star.fill" : "star")
.font(.title3)
.foregroundColor(value <= starRating ? .appSecondary : .secondary)
.foregroundStyle(value <= starRating ? Color.appSecondary : .secondary)
.padding(8)
.background(
Circle()
@@ -346,7 +346,7 @@ struct MonthlyCheckInView: View {
.padding(.horizontal, 10)
.padding(.vertical, 6)
.background(Color.gray.opacity(0.12))
.cornerRadius(AppConstants.UI.smallCornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.smallCornerRadius))
}
}
}
@@ -373,7 +373,7 @@ struct MonthlyCheckInView: View {
}
.padding()
.background(Color(.systemBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
}
@@ -386,7 +386,7 @@ struct MonthlyCheckInView: View {
VStack(alignment: .leading, spacing: 4) {
Text("Starting")
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
Text(viewModel.monthlySummary.formattedStartingValue)
.font(.subheadline.weight(.semibold))
}
@@ -394,7 +394,7 @@ struct MonthlyCheckInView: View {
VStack(alignment: .trailing, spacing: 4) {
Text("Ending")
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
Text(viewModel.monthlySummary.formattedEndingValue)
.font(.subheadline.weight(.semibold))
}
@@ -404,7 +404,7 @@ struct MonthlyCheckInView: View {
VStack(alignment: .leading, spacing: 4) {
Text("Contributions")
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
Text(viewModel.monthlySummary.formattedContributions)
.font(.subheadline.weight(.semibold))
}
@@ -412,16 +412,16 @@ struct MonthlyCheckInView: View {
VStack(alignment: .trailing, spacing: 4) {
Text("Net Performance")
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
Text("\(viewModel.monthlySummary.formattedNetPerformance) (\(viewModel.monthlySummary.formattedNetPerformancePercentage))")
.font(.subheadline.weight(.semibold))
.foregroundColor(viewModel.monthlySummary.netPerformance >= 0 ? .positiveGreen : .negativeRed)
.foregroundStyle(viewModel.monthlySummary.netPerformance >= 0 ? Color.positiveGreen : Color.negativeRed)
}
}
}
.padding()
.background(Color(.systemBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
}
@@ -489,7 +489,7 @@ struct MonthlyCheckInView: View {
}
.padding()
.background(Color(.systemBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
}
}
@@ -497,11 +497,11 @@ struct MonthlyCheckInView: View {
private func highlightRow(icon: String, iconColor: Color, label: String, name: String, percentage: Double, diff: Decimal, valueColor: Color) -> some View {
HStack {
Image(systemName: icon)
.foregroundColor(iconColor)
.foregroundStyle(iconColor)
VStack(alignment: .leading, spacing: 2) {
Text(label)
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
Text(name)
.font(.subheadline.weight(.semibold))
}
@@ -509,10 +509,10 @@ struct MonthlyCheckInView: View {
VStack(alignment: .trailing, spacing: 2) {
Text(String(format: "%+.1f%%", percentage))
.font(.subheadline.weight(.bold))
.foregroundColor(valueColor)
.foregroundStyle(valueColor)
Text("(\(diff.compactCurrencyString))")
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
}
}
@@ -529,18 +529,18 @@ struct MonthlyCheckInView: View {
} label: {
Label("Batch Update", systemImage: "square.and.pencil")
.font(.caption.weight(.semibold))
.foregroundColor(.appPrimary)
.foregroundStyle(Color.appPrimary)
}
}
Text("\(viewModel.sources.count)")
.font(.subheadline)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
if viewModel.sources.isEmpty {
Text("Add sources to start your monthly check-in.")
.font(.subheadline)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
} else {
ForEach(viewModel.sources, id: \.objectID) { source in
// Month-aware: "updated" means updated FOR THE VIEWED month.
@@ -575,7 +575,7 @@ struct MonthlyCheckInView: View {
.font(.subheadline.weight(.medium))
Text(updatedThisCycle ? "Updated this cycle" : "Needs update")
.font(.caption2)
.foregroundColor(updatedThisCycle ? .positiveGreen : .secondary)
.foregroundStyle(updatedThisCycle ? Color.positiveGreen : .secondary)
}
Spacer()
@@ -583,16 +583,16 @@ struct MonthlyCheckInView: View {
if let diff = valueDiff, updatedThisCycle {
Text(diff >= 0 ? "+\(diff.compactCurrencyString)" : diff.compactCurrencyString)
.font(.caption.weight(.semibold))
.foregroundColor(diff >= 0 ? .positiveGreen : .negativeRed)
.foregroundStyle(diff >= 0 ? Color.positiveGreen : Color.negativeRed)
}
Text(latestSnapshot?.date.relativeDayDescription ?? String(localized: "date_never"))
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
Image(systemName: "chevron.right")
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
}
.buttonStyle(.plain)
@@ -601,7 +601,7 @@ struct MonthlyCheckInView: View {
}
.padding()
.background(Color(.systemBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
}
@@ -614,7 +614,7 @@ struct MonthlyCheckInView: View {
.frame(minHeight: 120)
.padding(8)
.background(Color.gray.opacity(0.08))
.cornerRadius(12)
.clipShape(RoundedRectangle(cornerRadius: 12))
.focused($noteFocused)
.onChange(of: monthlyNote) { _, newValue in
MonthlyCheckInStore.setNote(newValue, for: referenceDate)
@@ -629,7 +629,7 @@ struct MonthlyCheckInView: View {
.frame(maxWidth: .infinity)
.padding(.vertical, 8)
.background(Color.appSecondary.opacity(0.12))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
}
.buttonStyle(.plain)
@@ -642,13 +642,13 @@ struct MonthlyCheckInView: View {
.frame(maxWidth: .infinity)
.padding(.vertical, 8)
.background(Color.appPrimary.opacity(0.12))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
}
}
}
.padding()
.background(Color(.systemBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
}
@@ -660,7 +660,7 @@ struct MonthlyCheckInView: View {
if viewModel.recentNotes.isEmpty {
Text("No snapshot notes for this month.")
.font(.subheadline)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
} else {
ForEach(viewModel.recentNotes, id: \.objectID) { snapshot in
VStack(alignment: .leading, spacing: 4) {
@@ -668,10 +668,10 @@ struct MonthlyCheckInView: View {
.font(.subheadline.weight(.semibold))
Text(snapshot.notes ?? "")
.font(.subheadline)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
Text(snapshot.date.friendlyDescription)
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
if snapshot.id != viewModel.recentNotes.last?.id {
@@ -682,7 +682,7 @@ struct MonthlyCheckInView: View {
}
.padding()
.background(Color(.systemBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
}
@@ -764,11 +764,11 @@ struct AchievementsView: View {
)
)
.font(.subheadline)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
.padding()
.background(Color(.systemBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
}
@@ -785,7 +785,7 @@ struct AchievementsView: View {
if let subtitle {
Text(subtitle)
.font(.subheadline)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
if achievements.isEmpty {
@@ -798,7 +798,7 @@ struct AchievementsView: View {
}
.padding()
.background(Color(.systemBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
}
@@ -810,7 +810,7 @@ struct AchievementsView: View {
.frame(width: 42, height: 42)
Image(systemName: status.achievement.icon)
.font(.headline)
.foregroundColor(isLocked ? .secondary : .appSecondary)
.foregroundStyle(isLocked ? .secondary : Color.appSecondary)
}
VStack(alignment: .leading, spacing: 2) {
@@ -818,7 +818,7 @@ struct AchievementsView: View {
.font(.subheadline.weight(.semibold))
Text(status.achievement.detail)
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
Spacer()
@@ -826,12 +826,12 @@ struct AchievementsView: View {
if isLocked {
Image(systemName: "lock.fill")
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
}
.padding(10)
.background(isLocked ? Color.gray.opacity(0.08) : Color.appSecondary.opacity(0.12))
.cornerRadius(AppConstants.UI.smallCornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.smallCornerRadius))
}
}
@@ -865,13 +865,13 @@ private extension MonthlyCheckInView {
HStack(alignment: .center, spacing: 8) {
Image(systemName: mood.iconName)
.font(.body)
.foregroundColor(isSelected ? moodColor(for: mood) : .secondary)
.foregroundStyle(isSelected ? moodColor(for: mood) : .secondary)
VStack(alignment: .leading, spacing: 2) {
Text(mood.title)
.font(.subheadline.weight(.semibold))
Text(mood.detail)
.font(.caption2)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
}
.padding(10)
@@ -880,7 +880,7 @@ private extension MonthlyCheckInView {
RoundedRectangle(cornerRadius: AppConstants.UI.smallCornerRadius)
.stroke(isSelected ? moodColor(for: mood) : Color.clear, lineWidth: 1)
)
.cornerRadius(AppConstants.UI.smallCornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.smallCornerRadius))
}
func moodColor(for mood: MonthlyCheckInMood) -> Color {
@@ -940,7 +940,7 @@ struct AchievementMilestoneBar: View {
if status.isUnlocked {
Image(systemName: "checkmark")
.font(.system(size: 8, weight: .bold))
.foregroundColor(.white)
.foregroundStyle(.white)
}
}
.position(x: x, y: barY)
@@ -989,10 +989,10 @@ struct BatchUpdateView: View {
.navigationTitle("Batch Update")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
ToolbarItem(placement: .topBarLeading) {
Button("Cancel") { dismiss() }
}
ToolbarItem(placement: .navigationBarTrailing) {
ToolbarItem(placement: .topBarTrailing) {
Button("Save") { saveAll() }
.disabled(filledCount == 0)
.fontWeight(.semibold)
@@ -1030,33 +1030,33 @@ struct BatchUpdateView: View {
if let prev = previousValue {
Text(prev.currencyString)
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
}
HStack {
Text(symbol)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
TextField("Current value", text: valueBinding)
.keyboardType(.decimalPad)
}
.padding(8)
.background(Color.gray.opacity(0.08))
.cornerRadius(8)
.clipShape(RoundedRectangle(cornerRadius: 8))
if isDetailed {
HStack {
Image(systemName: "plus.circle")
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
TextField("Contribution this period (optional)", text: contributionBinding)
.keyboardType(.decimalPad)
.font(.subheadline)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
.padding(8)
.background(Color.appSecondary.opacity(0.06))
.cornerRadius(8)
.clipShape(RoundedRectangle(cornerRadius: 8))
}
}
.padding(.vertical, 2)
@@ -21,7 +21,7 @@ struct MonthlySummaryShareView: View {
HStack {
Text(monthLabel)
.font(.caption.weight(.semibold))
.foregroundColor(.white.opacity(0.85))
.foregroundStyle(.white.opacity(0.85))
.textCase(.uppercase)
Spacer()
if streak >= 2 {
@@ -31,7 +31,7 @@ struct MonthlySummaryShareView: View {
Text(String(format: String(localized: "share_summary_streak_badge"), streak))
.font(.caption2.weight(.bold))
}
.foregroundColor(.white)
.foregroundStyle(.white)
.padding(.horizontal, 10)
.padding(.vertical, 5)
.background(Color.white.opacity(0.18))
@@ -41,17 +41,17 @@ struct MonthlySummaryShareView: View {
Text("Monthly Summary")
.font(.title.weight(.bold))
.foregroundColor(.white)
.foregroundStyle(.white)
.padding(.top, 6)
// Hero value
VStack(alignment: .leading, spacing: 6) {
Text("Portfolio Value")
.font(.subheadline.weight(.semibold))
.foregroundColor(.white.opacity(0.85))
.foregroundStyle(.white.opacity(0.85))
Text(totalValue)
.font(.system(size: 40, weight: .bold, design: .rounded))
.foregroundColor(.white)
.foregroundStyle(.white)
.minimumScaleFactor(0.6)
.lineLimit(1)
}
@@ -66,9 +66,9 @@ struct MonthlySummaryShareView: View {
Spacer()
Text("since last check-in")
.font(.caption)
.foregroundColor(.white.opacity(0.75))
.foregroundStyle(.white.opacity(0.75))
}
.foregroundColor(.white)
.foregroundStyle(.white)
.padding(.horizontal, 14)
.padding(.vertical, 10)
.background(Color.white.opacity(0.15))
@@ -85,14 +85,14 @@ struct MonthlySummaryShareView: View {
Text(mood.title)
.font(.subheadline.weight(.semibold))
}
.foregroundColor(.white)
.foregroundStyle(.white)
}
if let rating {
HStack(spacing: 2) {
ForEach(1...5, id: \.self) { i in
Image(systemName: i <= rating ? "star.fill" : "star")
.font(.caption)
.foregroundColor(.white.opacity(i <= rating ? 1 : 0.4))
.foregroundStyle(.white.opacity(i <= rating ? 1 : 0.4))
}
}
}
@@ -114,10 +114,10 @@ struct MonthlySummaryShareView: View {
VStack(alignment: .leading, spacing: 2) {
Text("Tracked with")
.font(.caption2.weight(.medium))
.foregroundColor(.white.opacity(0.7))
.foregroundStyle(.white.opacity(0.7))
Text(appName)
.font(.subheadline.weight(.bold))
.foregroundColor(.white)
.foregroundStyle(.white)
}
Spacer()
@@ -91,7 +91,7 @@ struct QuickUpdateView: View {
Image(systemName: "arrow.down.circle.fill")
}
.font(.subheadline.weight(.semibold))
.foregroundColor(.white)
.foregroundStyle(.white)
.padding(.vertical, 2)
}
.listRowBackground(Color.appPrimary)
@@ -243,14 +243,14 @@ struct QuickUpdateView: View {
.padding(.horizontal, 6)
.padding(.vertical, 1)
.background(Color.appSecondary.opacity(0.15))
.foregroundColor(.appSecondary)
.foregroundStyle(Color.appSecondary)
.clipShape(Capsule())
}
}
if source.latestValue != .zero {
Text(source.latestValue.currencyString)
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
}
Spacer()
@@ -269,7 +269,7 @@ struct QuickUpdateView: View {
HStack {
Text(String(localized: "quick_update_contribution_label"))
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
Spacer()
TextField(
String(localized: "quick_update_contribution_placeholder"),
@@ -279,7 +279,7 @@ struct QuickUpdateView: View {
.multilineTextAlignment(.trailing)
.frame(width: 120)
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
}
}
@@ -157,7 +157,7 @@ private func eyebrow(_ text: String) -> some View {
Text(text.uppercased())
.font(.caption2.weight(.bold))
.tracking(2.2)
.foregroundColor(ShareCardStyle.green)
.foregroundStyle(ShareCardStyle.green)
}
private func deltaChip(_ text: String, positive: Bool, large: Bool = false) -> some View {
@@ -167,7 +167,7 @@ private func deltaChip(_ text: String, positive: Bool, large: Bool = false) -> s
Text(text)
.font((large ? Font.headline : Font.caption).weight(.bold))
}
.foregroundColor(positive ? ShareCardStyle.green : Color(red: 1, green: 0.42, blue: 0.42))
.foregroundStyle(positive ? ShareCardStyle.green : Color(red: 1, green: 0.42, blue: 0.42))
.padding(.horizontal, large ? 12 : 9)
.padding(.vertical, large ? 6 : 4)
.background(
@@ -179,11 +179,11 @@ private func metricRow(_ title: String, _ value: String, theme: ShareCardTheme)
HStack {
Text(title)
.font(.caption.weight(.semibold))
.foregroundColor(theme.mutedInk)
.foregroundStyle(theme.mutedInk)
Spacer()
Text(value)
.font(.subheadline.weight(.semibold))
.foregroundColor(theme.ink)
.foregroundStyle(theme.ink)
.multilineTextAlignment(.trailing)
.minimumScaleFactor(0.7)
.lineLimit(1)
@@ -203,13 +203,13 @@ private func brandHeader(theme: ShareCardTheme, asOf: String?) -> some View {
VStack(alignment: .leading, spacing: 1) {
Text("Portfolio Journal")
.font(.system(.title2, design: .rounded).weight(.heavy))
.foregroundColor(theme.ink)
.foregroundStyle(theme.ink)
.minimumScaleFactor(0.7)
.lineLimit(1)
if let asOf {
Text(String(format: String(localized: "share_as_of"), asOf))
.font(.caption2.weight(.medium))
.foregroundColor(theme.mutedInk)
.foregroundStyle(theme.mutedInk)
}
}
Spacer(minLength: 0)
@@ -223,11 +223,11 @@ private func downloadFooter(qrCodeImage: UIImage?, theme: ShareCardTheme) -> som
VStack(alignment: .leading, spacing: 3) {
Text(String(localized: "share_cta_headline"))
.font(.subheadline.weight(.bold))
.foregroundColor(theme.ink)
.foregroundStyle(theme.ink)
.fixedSize(horizontal: false, vertical: true)
Text(String(localized: "share_cta_sub"))
.font(.caption2.weight(.medium))
.foregroundColor(theme.mutedInk)
.foregroundStyle(theme.mutedInk)
}
Spacer(minLength: 8)
@@ -295,13 +295,13 @@ struct PortfolioValueShareCardView: View {
eyebrow(String(localized: "share_hero_eyebrow"))
Text(heroPercent ?? totalValue)
.font(.system(size: storyFormat ? 52 : 44, weight: .heavy, design: .rounded))
.foregroundColor(heroIsPercent ? (isPositive ? ShareCardStyle.green : Color(red: 1, green: 0.42, blue: 0.42)) : theme.ink)
.foregroundStyle(heroIsPercent ? (isPositive ? ShareCardStyle.green : Color(red: 1, green: 0.42, blue: 0.42)) : theme.ink)
.minimumScaleFactor(0.5)
.lineLimit(1)
if heroIsPercent && !totalValue.isEmpty {
Text(totalValue)
.font(.title3.weight(.bold))
.foregroundColor(theme.ink)
.foregroundStyle(theme.ink)
.minimumScaleFactor(0.5)
.lineLimit(1)
}
@@ -363,7 +363,7 @@ struct MonthlyCheckInShareCardView: View {
eyebrow(summary.formattedMonthYear)
Text("Monthly Check-in")
.font(.title.weight(.bold))
.foregroundColor(theme.ink)
.foregroundStyle(theme.ink)
.padding(.top, 4)
VStack(spacing: 9) {
@@ -472,8 +472,8 @@ struct PortfolioShareOptionsView: View {
.frame(maxWidth: .infinity)
.padding(.vertical, 14)
.background(Color.appPrimary)
.foregroundColor(.white)
.cornerRadius(14)
.foregroundStyle(.white)
.clipShape(RoundedRectangle(cornerRadius: 14))
}
.padding(.horizontal)
.padding(.bottom, 8)