Skip to content

feat(connections): import and export connections on iOS#1738

Open
datlechin wants to merge 1 commit into
mainfrom
feat/ios-connection-import-export
Open

feat(connections): import and export connections on iOS#1738
datlechin wants to merge 1 commit into
mainfrom
feat/ios-connection-import-export

Conversation

@datlechin

Copy link
Copy Markdown
Member

What

Brings connection import (and export) to the iPhone app. A TestFlight user asked for a way to import connections on iOS; the Mac app already had a full import/export system, but iOS had none of it.

Approach

The serialization, crypto, and import-analysis logic lived inside the macOS app target. Rather than duplicate it on iOS (which would let a crypto fix drift between platforms), it moves into a new shared SPM target both apps use.

  • New TableProImport SPM target (Packages/TableProCore): the .tablepro envelope/exportable types, ConnectionExportCrypto (AES-256-GCM, PBKDF2 600k), and pure ConnectionImportAnalyzer + ConnectionImportDecoder. No AppKit; builds on macOS and iOS.
  • macOS rewired onto the shared types: removed its private crypto, routed through the shared decoder/analyzer, and changed the internal ImportItemStatus.duplicate associated value to drop the macOS-type dependency. All callers and tests updated in the same commit.
  • iOS import + export: .fileImporter from a new "more" menu, a native MobileConnectionImportSheet (preview, duplicate resolution, passphrase prompt for encrypted files), IOSConnectionImportService / IOSConnectionExportService, share-sheet export with an optional passphrase-encrypted credentials file, and .tablepro open-in from Files/AirDrop via UTExportedTypeDeclarations + CFBundleDocumentTypes and onOpenURL.

Secrets stay excluded by default; including passwords requires a passphrase that encrypts the file, matching Apple's Keychain guidance and the existing macOS behavior.

Tests

  • Shared TableProImportTests (15): crypto round-trip including wrong passphrase and corrupt header, envelope round-trip, analyzer duplicate/warning logic, path portability.
  • iOS IOSConnectionImportServiceTests (4): credential restore key format and mapped-indices-only, suggested filename.
  • macOS app and iOS app both build clean. SwiftLint --strict clean.

Notes

  • The macOS TableProTests target has a pre-existing local link failure in OracleConnectionErrorTests (needs the OracleNIO fork built); unrelated to this change. The edited macOS import tests compile.

@mintlify

mintlify Bot commented Jun 20, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟢 Ready View Preview Jun 20, 2026, 12:34 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2c63fb21b4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

port: ssh.port,
username: ssh.username,
authMethod: ssh.authMethod.rawValue,
privateKeyPath: PathPortability.contractHome(ssh.privateKeyPath ?? ""),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve pasted SSH private keys during export

When an iOS connection uses pasted private-key auth, the key material is stored separately in Keychain under com.TablePro.sshkeydata.<id> and IOSSSHProvider reloads it before connecting; this export only serializes privateKeyPath (which is empty for pasted keys), and the encrypted credentials path only includes passwords/passphrases. Re-importing even an encrypted export of such a connection therefore creates .privateKey auth with no key, so SSH tunnel creation fails with “No private key provided.”

Useful? React with 👍 / 👎.

}
}

let safeMode = exportable.safeModeLevel.flatMap { SafeModeLevel(rawValue: $0) } ?? .off

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Map imported safe-mode values instead of dropping them

When importing macOS exports with safeModeLevel values such as alert, alertFull, safeMode, or safeModeFull, this direct lookup uses the iOS enum (off, confirmWrites, readOnly), so those protective modes fall through to .off. The imported connection silently loses write-confirmation protection; map the macOS raw values to .confirmWrites or another supported iOS protection instead of defaulting them off.

Useful? React with 👍 / 👎.

Comment on lines +214 to +215
case "sshagent", "agent", "ssh agent": return .sshAgent
case "keyboardinteractive", "keyboard interactive": return .keyboardInteractive

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid importing unsupported SSH auth methods on iOS

When a macOS .tablepro export uses SSH Agent or Keyboard Interactive auth, these cases preserve that value on iOS, but SSHTunnelFactory.create only handles .password and .privateKey and throws for the default branch. That means the imported connection cannot connect on iOS; warn/skip these auth modes or map them to a supported editable state instead of saving an unsupported configuration.

Useful? React with 👍 / 👎.

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