Fix build errors, resolve all warnings, and improve code quality

Widget Extension Fixes:
- Create standalone WidgetDataProvider for widget data isolation
- Add WIDGET_EXTENSION compiler flag for conditional compilation
- Fix DataController references in widget-shared files
- Sync widget version numbers with main app (23, 1.0.2)
- Add WidgetBackground color to asset catalog

Warning Resolutions:
- Fix UIScreen.main deprecation in BGView and SharingListView
- Fix Text '+' concatenation deprecation in PurchaseButtonView and SettingsTabView
- Fix exhaustive switch in BiometricAuthManager (add .none case)
- Fix var to let in ExportService (3 instances)
- Fix unused result warning in NoteEditorView
- Fix ForEach duplicate ID warnings in MonthView and YearView

Code Quality Improvements:
- Wrap bypassSubscription in #if DEBUG for security
- Rename StupidAssCustomWidgetObservableObject to CustomWidgetStateViewModel
- Add @MainActor to IconViewModel
- Replace fatalError with graceful fallback in SharedModelContainer
- Add [weak self] to closures in DayViewViewModel
- Add OSLog-based AppLogger for production logging
- Add ImageCache with NSCache for memory efficiency
- Add AccessibilityHelpers with Reduce Motion support
- Create DataControllerProtocol for dependency injection
- Update .gitignore with secrets exclusions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-12-20 00:48:35 -06:00
parent 31a68b7102
commit 356ce9ea62
41 changed files with 1072 additions and 187 deletions

View File

@@ -0,0 +1,119 @@
//
// AccessibilityHelpers.swift
// Feels
//
// Accessibility utilities for supporting VoiceOver, Dynamic Type, and Reduce Motion.
//
import SwiftUI
// MARK: - Reduce Motion Support
/// Environment key for accessing reduce motion preference
struct ReduceMotionKey: EnvironmentKey {
static let defaultValue: Bool = false
}
extension EnvironmentValues {
var reduceMotion: Bool {
get { self[ReduceMotionKey.self] }
set { self[ReduceMotionKey.self] = newValue }
}
}
/// View modifier that respects reduce motion preference
struct ReduceMotionModifier: ViewModifier {
@Environment(\.accessibilityReduceMotion) var reduceMotion
let animation: Animation?
let reducedAnimation: Animation?
func body(content: Content) -> some View {
content
.animation(reduceMotion ? reducedAnimation : animation, value: UUID())
}
}
extension View {
/// Applies animation only when reduce motion is disabled
func accessibleAnimation(_ animation: Animation? = .default, reduced: Animation? = nil) -> some View {
modifier(ReduceMotionModifier(animation: animation, reducedAnimation: reduced))
}
/// Wraps content in withAnimation respecting reduce motion
func withAccessibleAnimation<V: Equatable>(_ animation: Animation? = .default, value: V, action: @escaping () -> Void) -> some View {
self.onChange(of: value) { _, _ in
if UIAccessibility.isReduceMotionEnabled {
action()
} else {
withAnimation(animation) {
action()
}
}
}
}
}
// MARK: - Accessibility Helpers
extension View {
/// Adds accessibility label with optional hint
func accessibleMoodCell(mood: Mood, date: Date) -> some View {
let formatter = DateFormatter()
formatter.dateStyle = .medium
return self
.accessibilityLabel("\(mood.strValue) on \(formatter.string(from: date))")
.accessibilityHint("Double tap to edit mood")
}
/// Makes a button accessible with custom label
func accessibleButton(label: String, hint: String? = nil) -> some View {
self
.accessibilityLabel(label)
.accessibilityHint(hint ?? "")
.accessibilityAddTraits(.isButton)
}
/// Groups related elements for VoiceOver
func accessibilityGrouped(label: String) -> some View {
self
.accessibilityElement(children: .combine)
.accessibilityLabel(label)
}
}
// MARK: - Dynamic Type Support
extension Font {
/// Returns a scalable font that respects Dynamic Type
static func scalable(_ style: Font.TextStyle, weight: Font.Weight = .regular) -> Font {
Font.system(style, design: .rounded).weight(weight)
}
}
extension View {
/// Ensures minimum touch target size for accessibility (44x44 points)
func accessibleTouchTarget() -> some View {
self.frame(minWidth: 44, minHeight: 44)
}
}
// MARK: - Accessibility Announcements
struct AccessibilityAnnouncement {
/// Announces a message to VoiceOver users
static func announce(_ message: String) {
UIAccessibility.post(notification: .announcement, argument: message)
}
/// Notifies VoiceOver that screen content has changed
static func screenChanged() {
UIAccessibility.post(notification: .screenChanged, argument: nil)
}
/// Notifies VoiceOver that layout has changed
static func layoutChanged(focusElement: Any? = nil) {
UIAccessibility.post(notification: .layoutChanged, argument: focusElement)
}
}