fix: don't attempt unlock with an empty password on first file pick

UnlockViewModel.openFile(url:) called unlockWithPassword() immediately
after saving the bookmark, but the first-open UI (openFileSection) has
no password field yet -- password is still "" at that point, so every
first file pick failed before the user could type anything. Picking a
file now only saves the bookmark; the view naturally switches to the
password-entry state for the user to unlock explicitly.

Also makes KDBXError conform to LocalizedError with readable messages
per case, so any error path that reaches the generic catch clause
shows something useful instead of the default
"The operation couldn't be completed (MyPassCore.KDBXError error N)".

Found while testing the Phase 4 UI against a real KDBX file in the
simulator.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WYqycDFsynHH9VnnK7LNSf
This commit is contained in:
2026-09-19 16:14:10 +02:00
co-authored by Claude Sonnet 5
parent ed6e4ab9ab
commit 82cc800415
2 changed files with 17 additions and 1 deletions
+2 -1
View File
@@ -78,7 +78,8 @@ final class UnlockViewModel: ObservableObject {
func openFile(url: URL) {
do {
try bookmarkService.save(url: url)
unlockWithPassword()
// Don't attempt to unlock yet -- picking a file only saves the bookmark.
// The view now shows the password field for the user to enter their password.
} catch {
errorMessage = error.localizedDescription
}
@@ -6,3 +6,18 @@ public enum KDBXError: Error, Equatable {
case parseError(String)
case writeError(String)
}
extension KDBXError: LocalizedError {
public var errorDescription: String? {
switch self {
case .invalidPassword:
return "Incorrect password."
case .fileNotFound:
return "The vault file could not be found."
case .parseError(let message):
return "Couldn't open the vault: \(message)"
case .writeError(let message):
return "Couldn't save the vault: \(message)"
}
}
}