Skip to content

Rebuild the last two non-Form screens, and drop the folder picker wrapper - #556

Merged
bradleymackey merged 1 commit into
mainfrom
native-standardization-forms
Sep 14, 2026
Merged

bradleymackey merged 1 commit into
mainfrom
native-standardization-forms

Conversation

@bradleymackey

Copy link
Copy Markdown
Member

OpenSourceView and VaultAutofillConfigurationView were the only two
screens in the app with no Form or List anywhere. Both faked
inset-grouped styling by hand.

OpenSourceView

A GeometryReader + ScrollView + VStack with
.frame(minHeight: geometry.size.height) to fake vertical centring, and
a bare Link styled with .foregroundStyle(.tint).

Now a Form matching VaultAboutView: a PlaceholderView header
section, a section holding the two paragraphs, and the GitHub link as a
FormRow row. The row uses the same purple glyph tile that
VaultAboutView already uses for its Open Source entry, so the two
screens agree.

VaultAutofillConfigurationView

A ScrollView + centred VStack + Spacer()s + a double
containerRelativeFrame, with two hand-drawn
RoundedRectangle(cornerRadius: 12).fill(.secondarySystemGroupedBackground)
"cards" imitating inset-grouped list rows.

Now a real List: the hero is a section, the two features are ordinary
Label rows (so they get the system's row separator and alignment), and
the Continue button moves into .safeAreaInset(edge: .bottom).

Fixed sizes on text are gone with it — .system(size: 28, weight: .bold)
on the title becomes .title.bold(), and the supporting line becomes
.headline. Both scale with Dynamic Type now. The 64pt header glyph
stays fixed, since it is decorative, but takes .tint instead of a
literal .blue.

FolderPickerView

AutoBackupSettingsView presented a .sheet containing a
UIViewControllerRepresentable around UIDocumentPickerViewController,
with its own Coordinator and delegate, purely to pick a folder.

.fileImporter(isPresented:allowedContentTypes: [.folder]) does this
natively, and BackupImportFlowView already uses .fileImporter
elsewhere. The wrapper and its coordinator are deleted (~30 lines).

Both APIs vend a security-scoped URL and configureSelectedProvider(with:)
is unchanged, so the handling either side of the picker is the same. A
cancelled pick is ignored rather than surfaced, matching the old delegate,
which only forwarded on didPickDocumentsAt.

Verification

Local, iPhone 18 Pro Max / iOS 27.0:

  • xcodebuild build-for-testing — TEST BUILD SUCCEEDED
  • Full suite, -parallel-testing-enabled NO — TEST EXECUTE SUCCEEDED,
    2834 passed, 0 failures across 24 bundles
  • 2 snapshots re-recorded (OpenSourceView,
    VaultAutofillConfigurationView) and visually reviewed
  • make format + make lint — clean

⚠️ The folder-picker change is not covered by an automated test — a
file importer cannot be driven from a snapshot test. It needs a manual
run: pick a folder, confirm the security-scoped bookmark still resolves
and that auto-backup writes to it.

⚠️ Automatic CI is still disabled (#548), so this is local verification only.

🤖 Generated with Claude Code

@bradleymackey
bradleymackey force-pushed the native-standardization-feed-toolbar branch from 4b4f990 to b581376 Compare September 14, 2026 16:49
Base automatically changed from native-standardization-feed-toolbar to main September 14, 2026 16:49
…pper

`OpenSourceView` and `VaultAutofillConfigurationView` were the only two
screens in the app with no `Form` or `List` anywhere. Both faked
inset-grouped styling by hand.

## OpenSourceView

A `GeometryReader` + `ScrollView` + `VStack` with
`.frame(minHeight: geometry.size.height)` to fake vertical centring, and
a bare `Link` styled with `.foregroundStyle(.tint)`.

Now a `Form` matching `VaultAboutView`: a `PlaceholderView` header
section, a section holding the two paragraphs, and the GitHub link as a
`FormRow` row. The row uses the same purple glyph tile that
`VaultAboutView` already uses for its Open Source entry, so the two
screens agree.

## VaultAutofillConfigurationView

A `ScrollView` + centred `VStack` + `Spacer()`s + a double
`containerRelativeFrame`, with two hand-drawn
`RoundedRectangle(cornerRadius: 12).fill(.secondarySystemGroupedBackground)`
"cards" imitating inset-grouped list rows.

Now a real `List`: the hero is a section, the two features are ordinary
`Label` rows (so they get the system's row separator and alignment), and
the Continue button moves into `.safeAreaInset(edge: .bottom)`.

Fixed sizes on text are gone with it — `.system(size: 28, weight: .bold)`
on the title becomes `.title.bold()`, and the supporting line becomes
`.headline`. Both scale with Dynamic Type now. The 64pt header glyph
stays fixed, since it is decorative, but takes `.tint` instead of a
literal `.blue`.

## FolderPickerView

`AutoBackupSettingsView` presented a `.sheet` containing a
`UIViewControllerRepresentable` around `UIDocumentPickerViewController`,
with its own `Coordinator` and delegate, purely to pick a folder.

`.fileImporter(isPresented:allowedContentTypes: [.folder])` does this
natively, and `BackupImportFlowView` already uses `.fileImporter`
elsewhere. The wrapper and its coordinator are deleted (~30 lines).

Both APIs vend a security-scoped URL and `configureSelectedProvider(with:)`
is unchanged, so the handling either side of the picker is the same. A
cancelled pick is ignored rather than surfaced, matching the old delegate,
which only forwarded on `didPickDocumentsAt`.

## Verification

Local, iPhone 18 Pro Max / iOS 27.0:

- `xcodebuild build-for-testing` — `TEST BUILD SUCCEEDED`
- Full suite, `-parallel-testing-enabled NO` — `TEST EXECUTE SUCCEEDED`,
  2834 passed, 0 failures across 24 bundles
- 2 snapshots re-recorded (`OpenSourceView`,
  `VaultAutofillConfigurationView`) and visually reviewed
- `make format` + `make lint` — clean

⚠️ The folder-picker change is **not** covered by an automated test — a
file importer cannot be driven from a snapshot test. It needs a manual
run: pick a folder, confirm the security-scoped bookmark still resolves
and that auto-backup writes to it.

⚠️ Automatic CI is still disabled (#548), so this is local verification only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@bradleymackey
bradleymackey force-pushed the native-standardization-forms branch from 9c52731 to 4c73962 Compare September 14, 2026 16:50
@bradleymackey
bradleymackey merged commit 25d4a8e into main Sep 14, 2026
@bradleymackey
bradleymackey deleted the native-standardization-forms branch September 14, 2026 16:50
bradleymackey added a commit that referenced this pull request Sep 15, 2026
## Summary

The backup surface was split across two sidebar peers ("Backup" and
"Restore"), with the Backup screen stacking four unrelated jobs —
password management, inline auto-backup settings, PDF export, and device
transfer — into one form. The password screen's "Generate Key" action
also read as password generation when it actually derives an encryption
key from a typed password. This PR restructures the IA:

- **Single "Backups" sidebar item** opening a hub: a last-backup status
banner (revives the orphaned `LastBackupSummaryView`) plus rows for
Auto-Backup, Export, Restore, and Backup Password. `BackupCreateView`
becomes `BackupExportView` (PDF + device transfer only); its
strings-only view model and nine orphaned xcstrings keys are deleted.
- **Backup password screen reframed**: the action is "Set Backup
Password" (revealed alongside the entry fields once a password is
typed), key derivation is presented as a progress state with distinct
cancelled/error copy, and the About text explains derivation without the
"generate" framing. Keygen cancellation and plaintext clearing are
untouched.
- **Auto-Backup gets its own screen** backed by a new
`AutoBackupViewModel` (the initial-state-injection refactor deferred in
#573): state is seeded synchronously from the service, provider states
are injectable for tests, the destination row shows provider name +
folder with a "Change" affordance, folder-configure failures surface as
an error row with a recovery suggestion (previously swallowed by an
empty `catch`), and the active provider resolves from configuration
instead of a hardcoded `availableProviders.first`.

## Manifesto review

Reviewed against `MANIFESTO.md`, corollary by corollary, since backups
are explicitly governed by it. This is a navigation and copy
restructure: backup payloads, encryption, and every action's mechanics
are untouched. **C1** — no bulk operation added; nothing touches
per-item safety fields. **C2/C3** — no new logging, telemetry, or error
channels near duress features; the one new error surface (auto-backup
folder configuration failures, previously swallowed by an empty `catch`)
reports storage-provider errors only. **C4** — device authentication
still gates every operation that loads or uses the backup key: export,
password set/change, the auto-backup screen, and restore imports all
authenticate exactly as before; the new un-gated hub adds navigation,
not capability. **C5** — the hub banner shows backup recency and kind,
never contents; no enumeration of protected items. **C6** — no undo, no
audit surface. **C7** — no protective default flipped; auto-backup
remains opt-in and its configuration remains behind device auth. **C8**
— no sensitive operation lost a step: merging the sidebar items removes
a hop between two navigation screens, while every export, restore, and
password flow retains its full sequence including authentication; the
password screen reframe changes verbs ("Generate Key" → "Set Backup
Password"), not gates. **C9** — backup surfaces never referenced duress
features; unchanged. **C10** — backup, export, and transfer payloads are
byte-identical. One visibility change flagged deliberately: the
last-backup banner (date + kind) is now visible without device auth,
where the old Backup screen showed nothing pre-auth. Weighed against
C4/C7: the same fact is already discoverable without auth (the Restore
screen is un-gated today; auto-backup files are visible in the Files
app), the banner reveals neither destination, contents, nor
protected-item structure, and surfacing backup staleness is itself a
data-loss protection.

## Testing

- Full `iOSAllTests` plan passes locally on iPhone 18 Pro Max / iOS
27.0.
- New suites: `AutoBackupViewModelTests` (19 tests),
`AutoBackupViewSnapshotTests` (7 scenarios — the coverage #573
deferred), `LastBackupSummaryViewSnapshotTests` (4, with an injectable
`now` for deterministic staleness).
- Re-recorded and visually reviewed: `BackupKeyChangeViewSnapshotTests`,
`BackupViewSnapshotTests` (hub + export),
`VaultMainNavigationViewSnapshotTests`.
- Simulator walk-through of the full flow passed: single sidebar entry,
hub layout, every push/back/sheet, and every auth gate failing closed.
- Not automatable (per #556): a real folder pick through `.fileImporter`
— needs one manual run with iCloud Drive.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant