Files
honeyDueKMP/iosApp/iosApp/Helpers/ViewStateHandler.swift
Trey t 9c574c4343 Harden iOS app with audit fixes, UI consistency, and sheet race condition fixes
Applies verified fixes from deep audit (concurrency, performance, security,
accessibility), standardizes CRUD form buttons to Add/Save pattern, removes
.drawingGroup() that broke search bar TextFields, and converts vulnerable
.sheet(isPresented:) + if-let patterns to safe presentation to prevent
blank white modals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 09:59:56 -06:00

113 lines
2.8 KiB
Swift

import SwiftUI
/// A view that handles loading, error, and success states with automatic error alerts
///
/// Example usage:
/// ```swift
/// ViewStateHandler(
/// isLoading: viewModel.isLoading,
/// error: viewModel.errorMessage,
/// onRetry: { viewModel.loadData() }
/// ) {
/// // Success content
/// List(items) { item in
/// Text(item.name)
/// }
/// }
/// ```
struct ViewStateHandler<Content: View>: View {
let isLoading: Bool
let error: String?
let onRetry: () -> Void
let content: Content
@State private var errorAlert: ErrorAlertInfo? = nil
init(
isLoading: Bool,
error: String?,
onRetry: @escaping () -> Void,
@ViewBuilder content: () -> Content
) {
self.isLoading = isLoading
self.error = error
self.onRetry = onRetry
self.content = content()
}
var body: some View {
ZStack {
if isLoading {
ProgressView()
.scaleEffect(1.5)
} else {
content
}
}
.onChange(of: error) { _, errorMessage in
if let errorMessage = errorMessage, !errorMessage.isEmpty {
errorAlert = ErrorAlertInfo(message: errorMessage)
}
}
.errorAlert(
error: errorAlert,
onRetry: {
errorAlert = nil
onRetry()
},
onDismiss: {
errorAlert = nil
}
)
}
}
/// Extension to add automatic error handling to any view
extension View {
/// Monitors an error message and shows error alert when it changes
///
/// Example usage:
/// ```swift
/// Form {
/// // Form fields
/// }
/// .handleErrors(
/// error: viewModel.errorMessage,
/// onRetry: { viewModel.submitForm() }
/// )
/// ```
func handleErrors(
error: String?,
onRetry: @escaping () -> Void
) -> some View {
modifier(ErrorHandlerModifier(error: error, onRetry: onRetry))
}
}
/// View modifier that handles errors automatically
private struct ErrorHandlerModifier: ViewModifier {
let error: String?
let onRetry: () -> Void
@State private var errorAlert: ErrorAlertInfo? = nil
func body(content: Content) -> some View {
content
.onChange(of: error) { _, errorMessage in
if let errorMessage = errorMessage, !errorMessage.isEmpty {
errorAlert = ErrorAlertInfo(message: errorMessage)
}
}
.errorAlert(
error: errorAlert,
onRetry: {
errorAlert = nil
onRetry()
},
onDismiss: {
errorAlert = nil
}
)
}
}