From 4d5d7dcf8c4f1acf1ac87053986d111f1bfd9ca7 Mon Sep 17 00:00:00 2001 From: dadachi Date: Fri, 16 Oct 2026 03:51:54 +0900 Subject: [PATCH] Dismiss the previous sign-in error when a new attempt starts Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01YDK5huvuRECyVrsQEdTN9q --- CHANGELOG.md | 1 + .../SignInEmailAndPasswordViewModel.swift | 4 ++++ .../SignInEmailAndPasswordViewModelTest.swift | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 987fdd3..0f63b7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Error messages are localized (English and Japanese). A request that fails without the API's `error_message` shows "Something went wrong while communicating with the server. Please try again later." (`NATIVEAPPTEMPLATE-2001`); developer-only errors (`NATIVEAPPTEMPLATE-1001`, `2002`–`2008`, `4001`–`4004`) show "An unexpected error occurred." The API's `error_message` is shown without the " [Status: nnn]" suffix. Error codes are unchanged; status codes, field names and underlying errors go to the log (`CodedError.debugDescription`) ### Fixed +- A failed sign-in's error message stayed on screen after the next attempt signed in; a new attempt now dismisses it - Debug builds lost the local API when launched other than from Xcode, e.g. from the Home Screen or when iOS relaunched the app after its language was changed in Settings: the endpoint came only from the scheme's environment variables, which only Xcode passes. It now comes from `API.xcconfig` (override it in the gitignored `API.local.xcconfig`) and is built into `Info.plist`; `NATIVEAPPTEMPLATE_API_*` environment variables still win. The scheme no longer sets them ### Added diff --git a/NativeAppTemplate/UI/App Root/SignInEmailAndPasswordViewModel.swift b/NativeAppTemplate/UI/App Root/SignInEmailAndPasswordViewModel.swift index 0cab50a..5f02810 100644 --- a/NativeAppTemplate/UI/App Root/SignInEmailAndPasswordViewModel.swift +++ b/NativeAppTemplate/UI/App Root/SignInEmailAndPasswordViewModel.swift @@ -66,6 +66,10 @@ final class SignInEmailAndPasswordViewModel { let theEmail = email.trimmingCharacters(in: whitespacesAndNewlines) let thePassword = password.trimmingCharacters(in: whitespacesAndNewlines) + // The previous attempt's error no longer applies, and would stay over the + // shops after this attempt signs in + messageBus.dismiss() + do { isLoggingIn = true try await sessionController.login(email: theEmail, password: thePassword) diff --git a/NativeAppTemplateTests/UI/App Root/SignInEmailAndPasswordViewModelTest.swift b/NativeAppTemplateTests/UI/App Root/SignInEmailAndPasswordViewModelTest.swift index 9ec21e0..c950e65 100644 --- a/NativeAppTemplateTests/UI/App Root/SignInEmailAndPasswordViewModelTest.swift +++ b/NativeAppTemplateTests/UI/App Root/SignInEmailAndPasswordViewModelTest.swift @@ -160,6 +160,25 @@ struct SignInEmailAndPasswordViewModelTest { #expect(viewModel.isLoggingIn == false) } + @Test("A new attempt dismisses the previous attempt's error") + func signInDismissesPreviousError() async { + let viewModel = SignInEmailAndPasswordViewModel( + sessionController: sessionController, + messageBus: messageBus + ) + // TestSessionController starts signed in; the wait below must see this sign-in + sessionController.userState = .notLoggedIn + messageBus.post(message: Message(error: NativeAppTemplateAPIError.requestFailed(nil, 401, "Invalid email or password."))) + + viewModel.email = "test@example.com" + viewModel.password = "password123" + viewModel.signIn() + + await waitUntil { sessionController.userState == .loggedIn && !viewModel.isLoggingIn } + + #expect(messageBus.messageVisible == false) + } + @Test func signInError() async { // Simulate an error by setting the sessionController to throw an error