Skip to content

refactor(ios): extract connection form logic into ConnectionFormViewModel#1165

Merged
datlechin merged 1 commit into
mainfrom
refactor/connection-form-viewmodel
May 9, 2026
Merged

refactor(ios): extract connection form logic into ConnectionFormViewModel#1165
datlechin merged 1 commit into
mainfrom
refactor/connection-form-viewmodel

Conversation

@datlechin
Copy link
Copy Markdown
Member

Summary

P1 #5 step 2 of 3: lift business logic out of ConnectionFormView (652 LOC) into a new ConnectionFormViewModel. The View drops to 392 LOC and now owns only UI state (file-picker flags, new-database alert flag, haptic triggers); the ViewModel owns form fields, validation, async operations, and persistence.

What moves into the ViewModel:

  • All form state: name, type, host, port, username, password, database, sslEnabled, groupId, tagId, safeModeLevel, plus the 11 SSH fields and selectedFileURL / newDatabaseName
  • Async operations: loadStoredCredentials(secureStore:) (replaces inline keychain reads in .task), testConnection(appState:secureStore:), save(appState:secureStore:) -> DatabaseConnection? returning the connection on success or nil with credentialError set
  • File picker handlers: handleSQLiteFilePicker(_:), handleSSHKeyFilePicker(_:), createNewDatabase(), clearSelectedFile()
  • Helpers: buildConnection(), copyToDocuments(_:), updateDefaultPort(), canSave, isEditing
  • Type-change reactivity uses a didSet on type to reset port and SQLite file selection, replacing the inline .onChange(of: type)
  • The KeyInputMode enum and the TestResult struct move to the VM where they belong

What stays in the View:

  • 5 @State: viewModel, activeFilePicker, pendingFilePicker, showNewDatabaseAlert, hapticSuccess, hapticError
  • The dual activeFilePicker + pendingFilePicker is preserved on purpose: .fileImporter resets activeFilePicker via the dismissal binding before the result closure fires, so the result handler reads pendingFilePicker instead
  • Form layout, sections, picker rows, and toolbar
  • The keychain warning alert binds to viewModel.credentialError != nil via a derived Binding<Bool> (no parallel @State flag)

Test plan

  • Open "New Connection": defaults populate (MySQL, 127.0.0.1:3306). Switch types: port updates (5432 for Postgres, 6379 for Redis, empty for SQLite), SQLite file selection clears
  • SQLite: Open Database File copies into Documents and prefills name; Create New Database prompts and assigns .db extension
  • SSH tunnel: toggle on, fill server, switch auth modes (password / private key), import key file (file persists into Documents prefixed with ssh_), paste key content
  • Test Connection (success): green checkmark + "Connection successful", success haptic. Failure: error message + recovery hint, error haptic
  • Save with valid credentials: parent receives the new DatabaseConnection via onSave, sheet dismisses
  • Save with keychain failure (force by toggling Keychain access): "Keychain Warning" alert appears, save aborts, sheet stays open
  • Edit existing connection: loadStoredCredentials hydrates password / SSH password / passphrase from Keychain on .task

@datlechin datlechin merged commit 649eca3 into main May 9, 2026
2 checks passed
@datlechin datlechin deleted the refactor/connection-form-viewmodel branch May 9, 2026 18:54
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