feat(ios): iPhone Duo readiness, and the side-by-side layout iPad gets with it - #3035
Merged
Merged
Conversation
… size content to the display
… display has room
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Makes the iOS app work on iPhone Duo, and gives iPad the same benefit, using only APIs that exist on the iOS 18 deployment target. No
#availablegate, no CI change.What was wrong
Measured on the iPhone Duo simulator (
iPhone19,4, iOS 27.1). The outer display is 466 x 678 pt, horizontal size class compact, vertical regular, scale 3. That is ~200pt shorter and ~64pt wider than an iPhone 17 Pro. The inner display is 669 x 951 pt and its native orientation is landscape.The system already moved the app's bars to the side, because every bar comes from a real
NavigationStackorTabViewtoolbar. Three controls did not survive that move, because a title-only or custom-view toolbar item is never presented vertically:Menulabel was anHStackofTextplus a chevron. On MySQL, PostgreSQL and SQL Server it is the only route to another database.Imagewith noButton, no title and no accessibility label. It is the only standing signal that writes are refused.Separately, the target read a size class zero times in 273 Swift files, so the 669pt inner display and every iPad rendered the same compact phone layout.
What changed
Toolbar items. Every bar item now carries a symbol and a title through
Label. That covers the three controls above plus Sort, Filter, More, Insert Row, Share, Edit and Add Tag, and it removes seven separate.accessibilityLabelpatches that existed only because the item was a bareImage. The Safe Mode badge became aSafeModeBadgevalue type, so its symbol, title and tint are unit tested rather than inlined in a ternary.Side-by-side layout. The Tables tab is a
NavigationSplitView: the table list is the sidebar, the browser is the detail. In compact width it collapses to exactly today's single-pane flow.ConnectionCoordinator.tablesPath(aNavigationPath) becameselectedTable, which is simpler for the deep-link, Handoff and Spotlight paths that feed it, and testable, which aNavigationPathis not.Content sized to the display.
DuoLayoutResolveris pure (Foundation and CoreGraphics only) and holds both decisions: how many fields a row preview shows (4 compact, 8 regular) and how tall the query editor may grow. The views are thin adapters that readhorizontalSizeClassand hand it over.Display scale.
1 / UIScreen.main.scalefor the keyboard bar's hairline became the view's owntraitCollection.displayScale, re-applied fromregisterForTraitChanges.UIScreen.mainis ambiguous on a two-display device and Apple documents it as heading for deprecation. The scale is clamped, because a detached view reports 0 and1 / 0is a constantNSLayoutConstraintrejects.Two things worth a second look
The detail column is keyed
.id(table). A split view detail is reused across selection changes, so without itDataBrowserView's@Stateview model would keep the previous table's rows. Keying it means picking another table builds a fresh browser, which is what the push did before. It does discard view state for the table you left.BottomSafeAreaBarLayoutTestsneeded repairing, and it was not my change that broke it. Its settling gate waited for the list's bottom inset to coverwindow.bounds.maxY - tabBarFrame.minY. Measured on Duo, the tab bar isx=397, y=0, w=69, h=678, a vertical strip on the trailing edge, so that band is the full 678pt window and the gate could never be satisfied. The gate is now edge-aware; both assertions were always device-independent and are unchanged. The suite went from 7s of spinning to 0.2s.Deliberately not here
ArrangementView,ReservedRegion,onHingeChange,toolbarVerticalEdge,ToolbarOverflowMenu,visibilityPriorityandpresentationPlacementare all iOS 27.0 or 27.1, and.github/workflows/ios-tests.ymlpins Xcode 26.4.1 onmacos-26, which has no 27.x SDK.#availablegates the runtime, not the SDK, so the first reference to any of them fails to compile on CI. That work is a follow-up behind a CI image move.onHingeChange. Apple scopes hinge data to interactions and effects and says layout comes from the arrangement and region APIs. A database client has no continuous-angle interaction.Verification
swiftlint --strict, changed filesdocs/scripts/check-writing-style.shdocs/scripts/check-docs-against-source.pyNew tests:
DuoLayoutResolverTests(10),SafeModeBadgeTests(5),TableSelectionResolverTests(6),HairlineThicknessTests(2),TablesSplitViewLayoutTests(2).TablesSplitViewLayoutTestsmounts the real shape (aNavigationSplitViewinside a.sidebarAdaptableTabView) and asserts two non-overlapping columns in regular width and one in compact. It drives the size class withUIHostingController.traitOverrides.horizontalSizeClass, which was an open question going in: it does propagate into SwiftUI layout, so regular width is testable without an iPad in CI.Not verified, and it cannot be from here.
Simulator.appis absent from this Xcode install andsimctlhas no pose, fold or hinge verb, so the inner display and the partially-open poses were never rendered. Every claim about the inner display rests on the measured device profile plus Apple's documentation, not on a screenshot. The outer display was captured with the app running. Pose behaviour needs a hand check in Xcode's Device Hub before release.The toolbar
Labelsubstitutions themselves are not unit tested: SwiftUIToolbarContentis not introspectable, and a test asserting a literal symbol name against the same literal proves nothing. They are covered by the build and by an outer-display capture.