Skip to content

fix(ios): stop ForEach indices race in row lists and validate driver port range#1094

Merged
datlechin merged 1 commit intomainfrom
fix/ios-crashes-array-and-port
May 7, 2026
Merged

fix(ios): stop ForEach indices race in row lists and validate driver port range#1094
datlechin merged 1 commit intomainfrom
fix/ios-crashes-array-and-port

Conversation

@datlechin
Copy link
Copy Markdown
Member

Summary

Two distinct production crashes hit by TestFlight users on TableProMobile build 10/11. Both fixed in this PR.

Crash 1 — Array._checkSubscript / "Index out of range"

Stack trace (build 11, iPhone 15, iOS 26.4.1):

Thread 0 Crashed:
0  Swift runtime failure: Index out of range
1  specialized Array._checkSubscript
2  specialized Array.subscript.getter
3  closure #1 in DataBrowserView.rowList.getter (DataBrowserView.swift:259)
4  ForEachChild.updateValue() (ForEach.swift:2068)

Root cause: ForEach(rows.indices, id: \.self) { index in ... rows[index] ... }. SwiftUI captures rows.indices once but rows is a computed property reading viewModel.legacyRows. When the array shrinks during a SwiftUI update pass (page change, filter, deletion), ForEach still iterates the stale indices and rows[index] traps. CLAUDE.md flags this exact pattern as a "Performance Pitfall".

Fix: introduce IndexedRow: Identifiable wrapper (Helpers/IndexedRow.swift) and iterate [IndexedRow] instead. SwiftUI diffs by stable id; if rows shrink, the missing ids are removed cleanly without ever subscripting a stale offset. Applied to all three sites: DataBrowserView, QueryEditorView, RowDetailView.

Crash 2 — Swift runtime failure: Not enough bits to represent the passed value

Stack trace (build 10, iPhone 16):

Thread 6 Crashed:
0  Swift runtime failure: Not enough bits to represent the passed value
1  MySQLActor.execute(_:) (MySQLDriver.swift:297)
2  MySQLDriver.execute(query:) (MySQLDriver.swift:56)
3  DataBrowserView.loadData(isInitial:) (DataBrowserView.swift:526)

Root cause: mysql_real_connect(..., UInt32(port), ...) where port: Int. If the connection's port is negative or > UInt32.max, UInt32(port) traps. The form parses port via Int(portString) ?? 3306 — a typed -1 slips through. Same shape exists in RedisDriver (Int32(port)).

Fix: validate port is in 1...65_535 before each driver-level integer cast and throw a readable connectionFailed instead of trapping. Applied to MySQL, Redis, and PostgreSQL (consistency — PG was string-interpolating so it didn't crash, but it silently passed garbage to libpq).

Test plan

  • Crash 1 manual repro: open a table with many rows, scroll, then quickly switch tables / change filter — no crash on rapid re-render.
  • Crash 2 manual repro: edit a connection, set port to -1 or 99999999999, save, tap connect. Expect a readable error sheet, no crash.
  • Regression: normal flows (open table, paginate, run query, view row detail, swipe through rows in RowDetailView) work as before.

Files changed

  • TableProMobile/Helpers/IndexedRow.swift (new) — Identifiable wrapper
  • TableProMobile/Views/DataBrowserView.swift — use IndexedRow.wrap
  • TableProMobile/Views/QueryEditorView.swift — use IndexedRow.wrap
  • TableProMobile/Views/RowDetailView.swift — use IndexedRow.wrap
  • TableProMobile/Drivers/MySQLDriver.swift — port range guard before UInt32(port)
  • TableProMobile/Drivers/RedisDriver.swift — port range guard before Int32(port)
  • TableProMobile/Drivers/PostgreSQLDriver.swift — port range guard (parity / readable error)
  • CHANGELOG.md — Fixed entries

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@datlechin datlechin merged commit f7ed420 into main May 7, 2026
1 check passed
@datlechin datlechin deleted the fix/ios-crashes-array-and-port branch May 7, 2026 14:41
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