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 {
|
||||
|
||||
// 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
|
||||
* If the error message is JSON, extract relevant error details
|
||||
* @param rawMessage The raw error message from the API
|
||||
* Handles network errors, JSON error responses, and raw error messages
|
||||
* @param rawMessage The raw error message from the API or exception
|
||||
* @return A user-friendly error message
|
||||
*/
|
||||
fun parse(rawMessage: String): String {
|
||||
val trimmed = rawMessage.trim()
|
||||
|
||||
// Check if the message looks like JSON (starts with { or [)
|
||||
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
|
||||
// Not JSON, return as-is
|
||||
return rawMessage
|
||||
// Check for network/connection errors first (these are technical messages from exceptions)
|
||||
for ((pattern, friendlyMessage) in networkErrorPatterns) {
|
||||
if (trimmed.contains(pattern, ignoreCase = true)) {
|
||||
return friendlyMessage
|
||||
}
|
||||
}
|
||||
|
||||
// If it's JSON, it's not meant for user display
|
||||
// Try to parse and extract meaningful error info
|
||||
// Check if it looks like a technical exception message
|
||||
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 {
|
||||
val jsonElement = Json.parseToJsonElement(trimmed)
|
||||
|
||||
@@ -51,4 +86,37 @@ object ErrorMessageParser {
|
||||
"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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user