feat(ios): add Shortcuts actions to insert table rows#1793
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2c4c8d2d9d
ℹ️ 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".
| rows: [PayloadRow] | ||
| ) async throws -> Int { | ||
| let columns = try await driver.fetchColumns(table: table, schema: schema) | ||
| let statements = try RowInsertPlanner.statements(table: table, type: type, columns: columns, rows: rows) |
There was a problem hiding this comment.
Qualify shortcut inserts with the selected schema
When a Shortcut selects a non-default schema on PostgreSQL/MSSQL/DuckDB, insert fetches columns using the resolved schema but then builds statements from only the bare table name. The resulting INSERT INTO "table" resolves against the connection's current/default schema, so it can write to public/dbo/main instead of the schema chosen in the picker, or fail if only the selected schema has that table. Pass the schema through to the insert SQL (or switch schema before executing) so the table picker target is the table that receives the rows.
Useful? React with 👍 / 👎.
| guard let connection = IntentConnectionLoader.connection(id: connectionId) else { | ||
| throw IntentDataError.connectionNotFound | ||
| } | ||
| if connection.safeModeLevel.writePermission == .blocked { |
There was a problem hiding this comment.
Honor confirm-writes before shortcut inserts
For connections set to Confirm Writes, writePermission is .requiresConfirmation, but this guard only blocks .blocked, so the Shortcut proceeds to write in the background after device unlock. Other mobile write paths such as QueryEditorView.executeQuery and InsertRowView.insertRow stop to confirm this state; without the same handling here, users relying on Confirm Writes can trigger an automation or voice shortcut that inserts rows without the required confirmation.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9ef26e7e67
ℹ️ 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".
| insertColumns.append(column.name) | ||
| insertValues.append(value.sqlValue) | ||
| } | ||
| guard !insertColumns.isEmpty else { return nil } |
There was a problem hiding this comment.
Reject uninsertable rows instead of skipping them
When an Add Rows payload contains a mix of valid rows and a row with no insertable values (for example {} or only an empty auto-generated primary key), returning nil inside this compactMap silently drops that input row while the remaining rows are inserted. The all-empty case errors later, but mixed batches report a smaller inserted count instead of failing the batch or identifying the bad row, so an automation can lose records without a clear error.
Useful? React with 👍 / 👎.
Signed-off-by: Ngô Quốc Đạt <datlechin@gmail.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5d74493066
ℹ️ 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".
| let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines) | ||
| guard !trimmed.isEmpty else { throw IntentDataError.emptyPayload } | ||
|
|
||
| let rows = trimmed.hasPrefix("{") || trimmed.hasPrefix("[") | ||
| ? try parseJSON(trimmed) | ||
| : try parseCSV(raw) |
There was a problem hiding this comment.
Strip BOMs before detecting the payload format
When the shortcut input comes from a UTF-8-with-BOM file or text, trimmingCharacters(in: .whitespacesAndNewlines) leaves the leading U+FEFF in place. In that case a JSON file no longer satisfies hasPrefix("{")/hasPrefix("[") and is parsed as CSV, while a CSV file keeps the BOM on the first header name (for example title), so column matching later reports an unknown column even though the file is otherwise valid. Strip a leading BOM before format detection/header parsing so common exported CSV/JSON files work.
Useful? React with 👍 / 👎.
Adds iOS Shortcuts support so users can append data to a table from the Shortcuts app, the Share Sheet, or by voice, without opening the app. Requested in discussion #1788.
What's new
Two background App Intents in TableProMobile:
Both use cascading pickers: Connection -> optional Database/Schema -> Table. The data is matched to the table's columns by name; columns you omit fall back to the database default (auto-increment keys, for example). Each action returns the inserted row count and speaks a short confirmation.
How it works
The intents reuse the app's existing headless path (ConnectionManager -> IOSDriverFactory -> driver), so they run in the background. Connection passwords come from the Keychain, and the action requires the device to be unlocked (
authenticationPolicy = .requiresAuthentication).Relational databases only (MySQL, MariaDB, PostgreSQL, Redshift, SQL Server, SQLite, DuckDB). Non-relational connections are refused with a clear error, as are read-only connections.
Security
Values flow through the same escaped-insert path the in-app Insert form uses. Identifiers are bounded: the table comes from the picker and column names are validated against the live schema, so unknown keys are rejected before any SQL is built. Capped at 10,000 rows per run.
Tests
RowPayloadTests- JSON object/array, CSV (quoted fields), null/bool handling, malformed and single-row errors.RowInsertPlannerTests- per-dialect quoting, empty-PK skip, NULL, unknown-column and no-column errors, quote escaping.IntentConnectionLoaderTests- decodes the full connection model.swiftlint --strict clean. Docs: new
external-api/ios-shortcutspage. CHANGELOG updated.Closes #1788