Add comprehensive error message parsing to prevent raw JSON display

- Created ErrorMessageParser utility for both iOS (Swift) and Android (Kotlin)
- Parser detects JSON-formatted error messages and extracts user-friendly text
- Identifies when data objects (not errors) are returned and provides generic messages
- Updated all API error handling to pass raw error bodies instead of concatenating
- Applied ErrorMessageParser across all ViewModels and screens on both platforms
- Fixed ContractorApi and DocumentApi to not concatenate error bodies with messages
- Updated ApiResultHandler to automatically parse all error messages
- Error messages now show "Request failed. Please check your input and try again." instead of raw JSON

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-11-14 22:59:42 -06:00
parent 225bdbc2bc
commit 2730c94e4d
48 changed files with 415 additions and 265 deletions

View File

@@ -89,7 +89,7 @@ struct JoinResidenceView: View {
break
} else if let error = state as? ApiResultError {
await MainActor.run {
viewModel.errorMessage = error.message
viewModel.errorMessage = ErrorMessageParser.parse(error.message)
viewModel.sharedViewModel.resetJoinResidenceState()
}
break

View File

@@ -100,7 +100,7 @@ struct ManageUsersView: View {
self.ownerId = responseData.ownerId as? Int32
self.isLoading = false
} else if let errorResult = result as? ApiResultError {
self.errorMessage = errorResult.message
self.errorMessage = ErrorMessageParser.parse(errorResult.message)
self.isLoading = false
} else {
self.errorMessage = "Failed to load users"
@@ -149,7 +149,7 @@ struct ManageUsersView: View {
self.shareCode = successResult.data
self.isGeneratingCode = false
} else if let errorResult = result as? ApiResultError {
self.errorMessage = errorResult.message
self.errorMessage = ErrorMessageParser.parse(errorResult.message)
self.isGeneratingCode = false
} else {
self.errorMessage = "Failed to generate share code"
@@ -177,7 +177,7 @@ struct ManageUsersView: View {
// Remove user from local list
self.users.removeAll { $0.id == userId }
} else if let errorResult = result as? ApiResultError {
self.errorMessage = errorResult.message
self.errorMessage = ErrorMessageParser.parse(errorResult.message)
} else {
self.errorMessage = "Failed to remove user"
}

View File

@@ -148,6 +148,10 @@ struct ResidenceDetailView: View {
.onAppear {
loadResidenceData()
}
.handleErrors(
error: viewModel.errorMessage,
onRetry: { loadResidenceData() }
)
}
}
@@ -158,10 +162,6 @@ private extension ResidenceDetailView {
var mainContent: some View {
if !hasAppeared || viewModel.isLoading {
loadingView
} else if let error = viewModel.errorMessage {
ErrorView(message: error) {
loadResidenceData()
}
} else if let residence = viewModel.selectedResidence {
contentView(for: residence)
}
@@ -328,7 +328,7 @@ private extension ResidenceDetailView {
if result is ApiResultSuccess<KotlinUnit> {
dismiss()
} else if let errorResult = result as? ApiResultError {
self.viewModel.errorMessage = errorResult.message
self.viewModel.errorMessage = ErrorMessageParser.parse(errorResult.message)
} else {
self.viewModel.errorMessage = "Failed to delete residence"
}

View File

@@ -44,7 +44,7 @@ class ResidenceViewModel: ObservableObject {
break
} else if let error = state as? ApiResultError {
await MainActor.run {
self.errorMessage = error.message
self.errorMessage = ErrorMessageParser.parse(error.message)
self.isLoading = false
}
break
@@ -74,7 +74,7 @@ class ResidenceViewModel: ObservableObject {
break
} else if let error = state as? ApiResultError {
await MainActor.run {
self.errorMessage = error.message
self.errorMessage = ErrorMessageParser.parse(error.message)
self.isLoading = false
}
break
@@ -93,7 +93,7 @@ class ResidenceViewModel: ObservableObject {
self.selectedResidence = success.data
self.isLoading = false
} else if let error = result as? ApiResultError {
self.errorMessage = error.message
self.errorMessage = ErrorMessageParser.parse(error.message)
self.isLoading = false
}
}
@@ -122,7 +122,7 @@ class ResidenceViewModel: ObservableObject {
break
} else if let error = state as? ApiResultError {
await MainActor.run {
self.errorMessage = error.message
self.errorMessage = ErrorMessageParser.parse(error.message)
self.isLoading = false
}
sharedViewModel.resetCreateState()
@@ -156,7 +156,7 @@ class ResidenceViewModel: ObservableObject {
break
} else if let error = state as? ApiResultError {
await MainActor.run {
self.errorMessage = error.message
self.errorMessage = ErrorMessageParser.parse(error.message)
self.isLoading = false
}
sharedViewModel.resetUpdateState()

View File

@@ -19,10 +19,6 @@ struct ResidencesListView: View {
.font(.body)
.foregroundColor(Color(.secondaryLabel))
}
} else if let error = viewModel.errorMessage {
ErrorView(message: error) {
viewModel.loadMyResidences()
}
} else if let response = viewModel.myResidences {
if response.residences.isEmpty {
EmptyResidencesView()
@@ -100,6 +96,10 @@ struct ResidencesListView: View {
.onAppear {
viewModel.loadMyResidences()
}
.handleErrors(
error: viewModel.errorMessage,
onRetry: { viewModel.loadMyResidences() }
)
}
}