v1.1 polish: accessibility, error logging, localization, and code quality sweep

- Wrap 30+ production print() statements in #if DEBUG guards across 18 files
- Add VoiceOver labels, hints, and traits to Watch app, Live Activities, widgets
- Add .accessibilityAddTraits(.isButton) to 15+ onTapGesture views
- Add text alternatives for color-only indicators (progress dots, mood circles)
- Localize raw string literals in NoteEditorView, EntryDetailView, widgets
- Replace 25+ silent try? with do/catch + AppLogger error logging
- Replace hardcoded font sizes with semantic Dynamic Type fonts
- Fix FIXME in IconPickerView (log icon change errors)
- Extract magic animation delays to named constants across 8 files
- Add widget empty state "Log your first mood!" messaging
- Hide decorative images from VoiceOver, add labels to ColorPickers
- Remove stale TODO in Color+Codable (alpha change deferred for migration)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey T
2026-03-26 20:09:14 -05:00
parent 4d9e906c4d
commit 1f040ab676
41 changed files with 427 additions and 107 deletions

View File

@@ -324,7 +324,9 @@ struct LiveActivityRecordingView: View {
try? FileManager.default.createDirectory(at: outputDir, withIntermediateDirectories: true)
exportPath = outputDir.path
#if DEBUG
print("📁 Exporting frames to: \(exportPath)")
#endif
let target = targetStreak
let outDir = outputDir
@@ -359,7 +361,9 @@ struct LiveActivityRecordingView: View {
await MainActor.run {
exportComplete = true
#if DEBUG
print("✅ Export complete! \(target) frames saved to: \(outPath)")
#endif
}
}
}

View File

@@ -9,6 +9,10 @@ import SwiftUI
import UniformTypeIdentifiers
import StoreKit
private enum SettingsAnimationConstants {
static let locationPermissionCheckDelay: TimeInterval = 1.0
}
// MARK: - Settings Content View (for use in SettingsTabView)
struct SettingsContentView: View {
@EnvironmentObject var authManager: BiometricAuthManager
@@ -437,7 +441,9 @@ struct SettingsContentView: View {
widgetExportPath = await WidgetExporter.exportAllWidgets()
isExportingWidgets = false
if let path = widgetExportPath {
#if DEBUG
print("📸 Widgets exported to: \(path.path)")
#endif
openInFilesApp(path)
}
}
@@ -490,7 +496,9 @@ struct SettingsContentView: View {
votingLayoutExportPath = await WidgetExporter.exportAllVotingLayouts()
isExportingVotingLayouts = false
if let path = votingLayoutExportPath {
#if DEBUG
print("📸 Voting layouts exported to: \(path.path)")
#endif
openInFilesApp(path)
}
}
@@ -543,7 +551,9 @@ struct SettingsContentView: View {
watchExportPath = await WatchExporter.exportAllWatchViews()
isExportingWatchViews = false
if let path = watchExportPath {
#if DEBUG
print("⌚ Watch views exported to: \(path.path)")
#endif
openInFilesApp(path)
}
}
@@ -596,7 +606,9 @@ struct SettingsContentView: View {
insightsExportPath = await InsightsExporter.exportInsightsScreenshots()
isExportingInsights = false
if let path = insightsExportPath {
#if DEBUG
print("✨ Insights exported to: \(path.path)")
#endif
openInFilesApp(path)
}
}
@@ -656,7 +668,9 @@ struct SettingsContentView: View {
sharingExportPath = await SharingScreenshotExporter.exportAllSharingScreenshots()
isGeneratingScreenshots = false
if let path = sharingExportPath {
#if DEBUG
print("📸 Sharing screenshots exported to: \(path.path)")
#endif
openInFilesApp(path)
}
}
@@ -916,7 +930,9 @@ struct SettingsContentView: View {
AnalyticsManager.shared.track(.healthKitNotAuthorized)
}
} catch {
#if DEBUG
print("HealthKit authorization failed: \(error)")
#endif
AnalyticsManager.shared.track(.healthKitEnableFailed)
}
}
@@ -1014,7 +1030,7 @@ struct SettingsContentView: View {
LocationManager.shared.requestAuthorization()
// Check if permission was denied after a brief delay
Task {
try? await Task.sleep(for: .seconds(1))
try? await Task.sleep(for: .seconds(SettingsAnimationConstants.locationPermissionCheckDelay))
let status = LocationManager.shared.authorizationStatus
if status == .denied || status == .restricted {
weatherEnabled = false
@@ -1445,9 +1461,13 @@ struct SettingsView: View {
switch result {
case .success(let url):
AnalyticsManager.shared.track(.dataExported(format: "file", count: 0))
#if DEBUG
print("Saved to \(url)")
#endif
case .failure(let error):
#if DEBUG
print(error.localizedDescription)
#endif
}
})
.fileImporter(isPresented: $showingImporter, allowedContentTypes: [.text],
@@ -1488,8 +1508,10 @@ struct SettingsView: View {
} catch {
// Handle failure.
AnalyticsManager.shared.track(.importFailed(error: error.localizedDescription))
#if DEBUG
print("Unable to read file contents")
print(error.localizedDescription)
#endif
}
}
}
@@ -1629,7 +1651,9 @@ struct SettingsView: View {
AnalyticsManager.shared.track(.healthKitNotAuthorized)
}
} catch {
#if DEBUG
print("HealthKit authorization failed: \(error)")
#endif
AnalyticsManager.shared.track(.healthKitEnableFailed)
}
}
@@ -1719,7 +1743,7 @@ struct SettingsView: View {
LocationManager.shared.requestAuthorization()
// Check if permission was denied after a brief delay
Task {
try? await Task.sleep(for: .seconds(1))
try? await Task.sleep(for: .seconds(SettingsAnimationConstants.locationPermissionCheckDelay))
let status = LocationManager.shared.authorizationStatus
if status == .denied || status == .restricted {
weatherEnabled = false
@@ -2280,9 +2304,13 @@ struct SettingsView: View {
let url = URL(fileURLWithPath: path)
do {
try image.jpegData(compressionQuality: 1.0)?.write(to: url, options: .atomic)
#if DEBUG
print(url)
#endif
} catch {
#if DEBUG
print(error.localizedDescription)
#endif
}
}