Files
scarf/scarf/Scarf iOS/App
Alan Wizemann 3da3d3ce5e fix(ios-rootmodel): surface store failures (A.3 + A.4 bundled)
Bundled because the fixes are coherent — they all add the same
mechanism (`lastError` + `os.Logger`) to the same model.

A.3 — Distinguish "no servers" from "Keychain unreachable":
- `RootModel.connect(to:)` previously used `try?` on `keyStore.load(for:)`.
  A biometric cancel or device-locked Keychain read returned nil → the
  app dropped the user into fresh onboarding, destroying the existing
  server's host/user/port. Now we catch the throw, log via os.Logger,
  set `lastError`, and stay on `.serverList`. The user sees a banner +
  Dismiss button instead of being kicked back to onboarding.
- `RootModel.load()` now logs the corrupted-blob path via os.Logger and
  sets `lastError` before falling through to onboarding (recovery is
  necessary, but the user gets context now).

A.4 — Surface delete failures in `forget()` and `disconnect()`:
- Both used `try?` on every store delete. On partial failure the
  in-memory dict was wiped while orphan Keychain entries lingered.
  Now each delete is `do/catch` with logging, failures collected into
  `lastError`. The in-memory state is reloaded from disk so it tracks
  what's actually persisted (covers the partial-failure case).

ServerListView gains an inline error banner above the list that reads
`model.lastError`, with a Dismiss button calling `clearLastError()`.

Verified: iOS build succeeds.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 07:50:52 +02:00
..