Add biometric lock and rate limit handling

Biometric lock: opt-in Face ID/Touch ID/fingerprint app lock with toggle
in ProfileScreen. Locks on background, requires auth on foreground return.
Platform implementations: BiometricPrompt (Android), LAContext (iOS).

Rate limit: 429 responses parsed with Retry-After header, user-friendly
error messages in all 10 locales, retry plugin respects 429.
ErrorMessageParser updated for both iOS Swift and KMM.
This commit is contained in:
Trey T
2026-03-26 14:37:04 -05:00
parent 334767cee7
commit 0d80df07f6
31 changed files with 871 additions and 7 deletions

View File

@@ -48,6 +48,11 @@ struct ErrorCodeMappingTests {
#expect(result == "Too many attempts. Please wait a few minutes and try again.")
}
@Test func tooManyRequestsCode() {
let result = ErrorMessageParser.parse("error.too_many_requests")
#expect(result == "Too many requests. Please try again later.")
}
@Test func taskNotFoundCode() {
let result = ErrorMessageParser.parse("error.task_not_found")
#expect(result == "Task not found. It may have been deleted.")
@@ -137,6 +142,16 @@ struct NetworkErrorPatternTests {
let result = ErrorMessageParser.parse("SocketTimeoutException: connect timed out")
#expect(result == "Request timed out. Please try again.")
}
@Test func tooManyRequestsDetected() {
let result = ErrorMessageParser.parse("Too many requests. Please try again later.")
#expect(result == "Too many requests. Please try again later.")
}
@Test func tooManyRequestsWithRetryAfterDetected() {
let result = ErrorMessageParser.parse("Too many requests. Please try again in 30 seconds.")
#expect(result.contains("Too many requests"))
}
}
// MARK: - Technical Error Detection Tests

View File

@@ -28,6 +28,7 @@ enum ErrorMessageParser {
// Password reset errors
"error.rate_limit_exceeded": "Too many attempts. Please wait a few minutes and try again.",
"error.too_many_requests": "Too many requests. Please try again later.",
"error.too_many_attempts": "Too many attempts. Please request a new code.",
"error.invalid_reset_token": "This reset link has expired. Please request a new one.",
"error.password_reset_failed": "Password reset failed. Please try again.",
@@ -148,7 +149,8 @@ enum ErrorMessageParser {
("ConnectException", "Unable to connect. Please check your internet connection."),
("SocketTimeoutException", "Request timed out. Please try again."),
("Connection refused", "Unable to connect. The server may be temporarily unavailable."),
("Connection reset", "Connection lost. Please try again.")
("Connection reset", "Connection lost. Please try again."),
("Too many requests", "Too many requests. Please try again later.")
]
// MARK: - Technical Error Indicators