Improve error message handling with user-friendly messages
- Add ErrorMessageParser in Kotlin and Swift to detect network errors and technical messages, replacing them with human-readable text - Update all ViewModels to use ErrorMessageParser.parse() for error display - Remove redundant error popup from LoginView (error shows inline only) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,23 +9,58 @@ import kotlinx.serialization.json.jsonPrimitive
|
|||||||
*/
|
*/
|
||||||
object ErrorMessageParser {
|
object ErrorMessageParser {
|
||||||
|
|
||||||
|
// Network/connection error patterns to detect
|
||||||
|
private val networkErrorPatterns = listOf(
|
||||||
|
"Could not connect to the server" to "Unable to connect to the server. Please check your internet connection.",
|
||||||
|
"NSURLErrorDomain" to "Unable to connect to the server. Please check your internet connection.",
|
||||||
|
"UnresolvedAddressException" to "Unable to connect to the server. Please check your internet connection.",
|
||||||
|
"ConnectException" to "Unable to connect to the server. Please check your internet connection.",
|
||||||
|
"SocketTimeoutException" to "Request timed out. Please try again.",
|
||||||
|
"TimeoutException" to "Request timed out. Please try again.",
|
||||||
|
"No address associated" to "Unable to connect to the server. Please check your internet connection.",
|
||||||
|
"Network is unreachable" to "No internet connection. Please check your network settings.",
|
||||||
|
"Connection refused" to "Unable to connect to the server. The server may be down.",
|
||||||
|
"Connection reset" to "Connection was interrupted. Please try again.",
|
||||||
|
"SSLHandshakeException" to "Secure connection failed. Please try again.",
|
||||||
|
"CertificateException" to "Secure connection failed. Please try again.",
|
||||||
|
"UnknownHostException" to "Unable to connect to the server. Please check your internet connection.",
|
||||||
|
"java.net.SocketException" to "Connection error. Please try again.",
|
||||||
|
"CFNetwork" to "Unable to connect to the server. Please check your internet connection.",
|
||||||
|
"kCFStreamError" to "Unable to connect to the server. Please check your internet connection.",
|
||||||
|
"Code=-1004" to "Unable to connect to the server. Please check your internet connection.",
|
||||||
|
"Code=-1009" to "No internet connection. Please check your network settings.",
|
||||||
|
"Code=-1001" to "Request timed out. Please try again."
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses error messages to extract user-friendly text
|
* Parses error messages to extract user-friendly text
|
||||||
* If the error message is JSON, extract relevant error details
|
* Handles network errors, JSON error responses, and raw error messages
|
||||||
* @param rawMessage The raw error message from the API
|
* @param rawMessage The raw error message from the API or exception
|
||||||
* @return A user-friendly error message
|
* @return A user-friendly error message
|
||||||
*/
|
*/
|
||||||
fun parse(rawMessage: String): String {
|
fun parse(rawMessage: String): String {
|
||||||
val trimmed = rawMessage.trim()
|
val trimmed = rawMessage.trim()
|
||||||
|
|
||||||
// Check if the message looks like JSON (starts with { or [)
|
// Check for network/connection errors first (these are technical messages from exceptions)
|
||||||
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
|
for ((pattern, friendlyMessage) in networkErrorPatterns) {
|
||||||
// Not JSON, return as-is
|
if (trimmed.contains(pattern, ignoreCase = true)) {
|
||||||
return rawMessage
|
return friendlyMessage
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's JSON, it's not meant for user display
|
// Check if it looks like a technical exception message
|
||||||
// Try to parse and extract meaningful error info
|
if (isTechnicalError(trimmed)) {
|
||||||
|
return "Something went wrong. Please try again."
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the message looks like JSON (starts with { or [)
|
||||||
|
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
|
||||||
|
// Not JSON - if it's a short, readable message, return it
|
||||||
|
// Otherwise return a generic message
|
||||||
|
return if (isUserFriendly(trimmed)) trimmed else "Something went wrong. Please try again."
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's JSON, try to parse and extract meaningful error info
|
||||||
return try {
|
return try {
|
||||||
val jsonElement = Json.parseToJsonElement(trimmed)
|
val jsonElement = Json.parseToJsonElement(trimmed)
|
||||||
|
|
||||||
@@ -51,4 +86,37 @@ object ErrorMessageParser {
|
|||||||
"An error occurred. Please try again."
|
"An error occurred. Please try again."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the message looks like a technical/developer error (stack trace, exception, etc)
|
||||||
|
*/
|
||||||
|
private fun isTechnicalError(message: String): Boolean {
|
||||||
|
val technicalIndicators = listOf(
|
||||||
|
"Exception",
|
||||||
|
"Error Domain=",
|
||||||
|
"UserInfo=",
|
||||||
|
"at com.",
|
||||||
|
"at org.",
|
||||||
|
"at java.",
|
||||||
|
"at kotlin.",
|
||||||
|
"at io.",
|
||||||
|
"Caused by:",
|
||||||
|
"Stack trace:",
|
||||||
|
".kt:",
|
||||||
|
".java:",
|
||||||
|
"0x",
|
||||||
|
"Code=",
|
||||||
|
"interface:",
|
||||||
|
"LocalDataTask"
|
||||||
|
)
|
||||||
|
return technicalIndicators.any { message.contains(it, ignoreCase = true) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a message is user-friendly (short, no technical jargon)
|
||||||
|
*/
|
||||||
|
private fun isUserFriendly(message: String): Boolean {
|
||||||
|
// If it's short and doesn't contain technical indicators, it's probably user-friendly
|
||||||
|
return message.length < 200 && !isTechnicalError(message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ class ContractorSharingManager: ObservableObject {
|
|||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.importError = error.localizedDescription
|
self.importError = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isImporting = false
|
self.isImporting = false
|
||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class ContractorViewModel: ObservableObject {
|
|||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ class ContractorViewModel: ObservableObject {
|
|||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@ class ContractorViewModel: ObservableObject {
|
|||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isCreating = false
|
self.isCreating = false
|
||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ class ContractorViewModel: ObservableObject {
|
|||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isUpdating = false
|
self.isUpdating = false
|
||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
@@ -156,7 +156,7 @@ class ContractorViewModel: ObservableObject {
|
|||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isDeleting = false
|
self.isDeleting = false
|
||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
@@ -176,7 +176,7 @@ class ContractorViewModel: ObservableObject {
|
|||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class DocumentViewModel: ObservableObject {
|
|||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,7 +135,7 @@ class DocumentViewModel: ObservableObject {
|
|||||||
completion(false, self.errorMessage)
|
completion(false, self.errorMessage)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
completion(false, self.errorMessage)
|
completion(false, self.errorMessage)
|
||||||
}
|
}
|
||||||
@@ -203,7 +203,7 @@ class DocumentViewModel: ObservableObject {
|
|||||||
completion(false, self.errorMessage)
|
completion(false, self.errorMessage)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
completion(false, self.errorMessage)
|
completion(false, self.errorMessage)
|
||||||
}
|
}
|
||||||
@@ -228,7 +228,7 @@ class DocumentViewModel: ObservableObject {
|
|||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,82 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// Utility for parsing and cleaning error messages from API responses
|
/// Utility for parsing and cleaning error messages from API responses and network errors
|
||||||
enum ErrorMessageParser {
|
enum ErrorMessageParser {
|
||||||
|
|
||||||
|
// Network/connection error patterns to detect
|
||||||
|
private static let networkErrorPatterns: [(pattern: String, message: String)] = [
|
||||||
|
("Could not connect to the server", "Unable to connect to the server. Please check your internet connection."),
|
||||||
|
("NSURLErrorDomain", "Unable to connect to the server. Please check your internet connection."),
|
||||||
|
("The Internet connection appears to be offline", "No internet connection. Please check your network settings."),
|
||||||
|
("A server with the specified hostname could not be found", "Unable to connect to the server. Please check your internet connection."),
|
||||||
|
("The request timed out", "Request timed out. Please try again."),
|
||||||
|
("The network connection was lost", "Connection was interrupted. Please try again."),
|
||||||
|
("An SSL error has occurred", "Secure connection failed. Please try again."),
|
||||||
|
("CFNetwork", "Unable to connect to the server. Please check your internet connection."),
|
||||||
|
("kCFStreamError", "Unable to connect to the server. Please check your internet connection."),
|
||||||
|
("Code=-1004", "Unable to connect to the server. Please check your internet connection."),
|
||||||
|
("Code=-1009", "No internet connection. Please check your network settings."),
|
||||||
|
("Code=-1001", "Request timed out. Please try again."),
|
||||||
|
("Code=-1003", "Unable to connect to the server. Please check your internet connection."),
|
||||||
|
("Code=-1005", "Connection was interrupted. Please try again."),
|
||||||
|
("Code=-1200", "Secure connection failed. Please try again."),
|
||||||
|
("UnresolvedAddressException", "Unable to connect to the server. Please check your internet connection."),
|
||||||
|
("ConnectException", "Unable to connect to the server. Please check your internet connection."),
|
||||||
|
("SocketTimeoutException", "Request timed out. Please try again."),
|
||||||
|
("Connection refused", "Unable to connect to the server. The server may be down."),
|
||||||
|
("Connection reset", "Connection was interrupted. Please try again.")
|
||||||
|
]
|
||||||
|
|
||||||
|
// Indicators that a message is technical/developer-facing
|
||||||
|
private static let technicalIndicators = [
|
||||||
|
"Exception",
|
||||||
|
"Error Domain=",
|
||||||
|
"UserInfo=",
|
||||||
|
"at com.",
|
||||||
|
"at org.",
|
||||||
|
"at java.",
|
||||||
|
"at kotlin.",
|
||||||
|
"at io.",
|
||||||
|
"Caused by:",
|
||||||
|
"Stack trace:",
|
||||||
|
".kt:",
|
||||||
|
".java:",
|
||||||
|
".swift:",
|
||||||
|
"0x",
|
||||||
|
"Code=",
|
||||||
|
"interface:",
|
||||||
|
"LocalDataTask",
|
||||||
|
"NSUnderlyingError",
|
||||||
|
"_kCF"
|
||||||
|
]
|
||||||
|
|
||||||
/// Parses error messages to extract user-friendly text
|
/// Parses error messages to extract user-friendly text
|
||||||
/// If the error message is JSON, extract relevant error details
|
/// Handles network errors, JSON error responses, and raw error messages
|
||||||
/// - Parameter rawMessage: The raw error message from the API
|
/// - Parameter rawMessage: The raw error message from the API or exception
|
||||||
/// - Returns: A user-friendly error message
|
/// - Returns: A user-friendly error message
|
||||||
static func parse(_ rawMessage: String) -> String {
|
static func parse(_ rawMessage: String) -> String {
|
||||||
// Check if the message looks like JSON (starts with { or [)
|
|
||||||
let trimmed = rawMessage.trimmingCharacters(in: .whitespacesAndNewlines)
|
let trimmed = rawMessage.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
guard trimmed.hasPrefix("{") || trimmed.hasPrefix("[") else {
|
|
||||||
// Not JSON, return as-is
|
// Check for network/connection errors first (these are technical messages from exceptions)
|
||||||
return rawMessage
|
for (pattern, friendlyMessage) in networkErrorPatterns {
|
||||||
|
if trimmed.localizedCaseInsensitiveContains(pattern) {
|
||||||
|
return friendlyMessage
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's JSON, it's not meant for user display
|
// Check if it looks like a technical exception message
|
||||||
// Try to parse and extract meaningful error info
|
if isTechnicalError(trimmed) {
|
||||||
|
return "Something went wrong. Please try again."
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the message looks like JSON (starts with { or [)
|
||||||
|
guard trimmed.hasPrefix("{") || trimmed.hasPrefix("[") else {
|
||||||
|
// Not JSON - if it's a short, readable message, return it
|
||||||
|
// Otherwise return a generic message
|
||||||
|
return isUserFriendly(trimmed) ? trimmed : "Something went wrong. Please try again."
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's JSON, try to parse and extract meaningful error info
|
||||||
guard let data = trimmed.data(using: .utf8) else {
|
guard let data = trimmed.data(using: .utf8) else {
|
||||||
return "An error occurred. Please try again."
|
return "An error occurred. Please try again."
|
||||||
}
|
}
|
||||||
@@ -47,4 +107,15 @@ enum ErrorMessageParser {
|
|||||||
// If we couldn't parse or extract a message, return a generic error
|
// If we couldn't parse or extract a message, return a generic error
|
||||||
return "An error occurred. Please try again."
|
return "An error occurred. Please try again."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if the message looks like a technical/developer error (stack trace, exception, etc)
|
||||||
|
private static func isTechnicalError(_ message: String) -> Bool {
|
||||||
|
return technicalIndicators.contains { message.localizedCaseInsensitiveContains($0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a message is user-friendly (short, no technical jargon)
|
||||||
|
private static func isUserFriendly(_ message: String) -> Bool {
|
||||||
|
// If it's short and doesn't contain technical indicators, it's probably user-friendly
|
||||||
|
return message.count < 200 && !isTechnicalError(message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ class AppleSignInViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,7 +115,7 @@ class AppleSignInViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
errorMessage = appleError.errorDescription
|
errorMessage = appleError.errorDescription
|
||||||
} else {
|
} else {
|
||||||
errorMessage = error.localizedDescription
|
errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -352,10 +352,6 @@ struct LoginView: View {
|
|||||||
showPasswordReset = true
|
showPasswordReset = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.handleErrors(
|
|
||||||
error: viewModel.errorMessage,
|
|
||||||
onRetry: { viewModel.login() }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ class LoginViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class PasswordResetViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,7 @@ class PasswordResetViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,7 +155,7 @@ class PasswordResetViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ class NotificationPreferencesViewModelWrapper: ObservableObject {
|
|||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -435,7 +435,7 @@ class NotificationPreferencesViewModelWrapper: ObservableObject {
|
|||||||
self.isSaving = false
|
self.isSaving = false
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isSaving = false
|
self.isSaving = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ class ProfileViewModel: ObservableObject {
|
|||||||
self.isLoadingUser = false
|
self.isLoadingUser = false
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoadingUser = false
|
self.isLoadingUser = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ class ProfileViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.successMessage = nil
|
self.successMessage = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ class RegisterViewModel: ObservableObject {
|
|||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ struct ManageUsersView: View {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,7 +162,7 @@ struct ManageUsersView: View {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isGeneratingCode = false
|
self.isGeneratingCode = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -188,7 +188,7 @@ struct ManageUsersView: View {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -468,7 +468,7 @@ private extension ResidenceDetailView {
|
|||||||
} catch {
|
} catch {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
self.isDeleting = false
|
self.isDeleting = false
|
||||||
self.viewModel.errorMessage = error.localizedDescription
|
self.viewModel.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -500,7 +500,7 @@ private extension ResidenceDetailView {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
self.contractorsError = error.localizedDescription
|
self.contractorsError = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoadingContractors = false
|
self.isLoadingContractors = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ class ResidenceSharingManager: ObservableObject {
|
|||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isImporting = false
|
self.isImporting = false
|
||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class ResidenceViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,7 +105,7 @@ class ResidenceViewModel: ObservableObject {
|
|||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,7 +127,7 @@ class ResidenceViewModel: ObservableObject {
|
|||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,7 +176,7 @@ class ResidenceViewModel: ObservableObject {
|
|||||||
} catch {
|
} catch {
|
||||||
print("🏠 ResidenceVM: Exception: \(error)")
|
print("🏠 ResidenceVM: Exception: \(error)")
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
completion(nil)
|
completion(nil)
|
||||||
}
|
}
|
||||||
@@ -205,7 +205,7 @@ class ResidenceViewModel: ObservableObject {
|
|||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
@@ -228,7 +228,7 @@ class ResidenceViewModel: ObservableObject {
|
|||||||
self.isGeneratingReport = false
|
self.isGeneratingReport = false
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.reportMessage = error.localizedDescription
|
self.reportMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isGeneratingReport = false
|
self.isGeneratingReport = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,7 +263,7 @@ class ResidenceViewModel: ObservableObject {
|
|||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ class TaskViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.actionState = .error(.create, error.localizedDescription)
|
self.actionState = .error(.create, error.localizedDescription)
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,7 +112,7 @@ class TaskViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.actionState = .error(.cancel, error.localizedDescription)
|
self.actionState = .error(.cancel, error.localizedDescription)
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,7 +136,7 @@ class TaskViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.actionState = .error(.uncancel, error.localizedDescription)
|
self.actionState = .error(.uncancel, error.localizedDescription)
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,7 +160,7 @@ class TaskViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.actionState = .error(.markInProgress, error.localizedDescription)
|
self.actionState = .error(.markInProgress, error.localizedDescription)
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,7 +184,7 @@ class TaskViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.actionState = .error(.archive, error.localizedDescription)
|
self.actionState = .error(.archive, error.localizedDescription)
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,7 +208,7 @@ class TaskViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.actionState = .error(.unarchive, error.localizedDescription)
|
self.actionState = .error(.unarchive, error.localizedDescription)
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,7 +232,7 @@ class TaskViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.actionState = .error(.update, error.localizedDescription)
|
self.actionState = .error(.update, error.localizedDescription)
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,7 +268,7 @@ class TaskViewModel: ObservableObject {
|
|||||||
self.isLoadingCompletions = false
|
self.isLoadingCompletions = false
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.completionsError = error.localizedDescription
|
self.completionsError = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoadingCompletions = false
|
self.isLoadingCompletions = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,7 +360,7 @@ class TaskViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
self.tasksError = error.localizedDescription
|
self.tasksError = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoadingTasks = false
|
self.isLoadingTasks = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class VerifyEmailViewModel: ObservableObject {
|
|||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user