A reader opens an article, unfolds their phone, and expects to keep reading. The library can become a sidebar and the detail can gain space, but changing the layout should not require choosing the article again. That makes navigation state a useful starting point when preparing a SwiftUI app for iPhone Duo.
Apple's Prepare your app for iPhone Duo recommends adaptive navigation and size classes. For a library-and-reader interface, a practical implementation is one NavigationSplitView with a shared selection, followed by tests that change the available space while an item is open.
Let the navigation container adapt
The session describes the inner display as regular in both size-class dimensions. A rule such as "iPhone means one column" therefore cannot express the available layout. Orientation is also an unreliable substitute: the inner display does not honor the app's supported interface orientations.
NavigationSplitView already supports a sidebar and detail, and collapses its columns into a stack in narrow contexts. Use that behavior before introducing separate compact and expanded navigation roots. Read horizontalSizeClass when a custom component needs a different arrangement, without making it responsible for replacing the entire navigation hierarchy.
Keeping the root stable also reduces the number of paths to maintain. Opening a library item can follow the same selection logic whether the sidebar is visible beside the reader or the list occupies the screen.
Give selection one owner
For this example, the selected entry belongs to the view containing both columns. The list and its navigation links use the same stable identifier type:
import SwiftUI
struct LibraryEntry: Identifiable {
let id: Int
let title: String
}
struct LibraryView: View {
private let entries = [
LibraryEntry(id: 1, title: "Layout notes"),
LibraryEntry(id: 2, title: "Navigation notes")
]
@State private var selectedID: LibraryEntry.ID?
var body: some View {
NavigationSplitView {
List(entries, selection: $selectedID) { entry in
NavigationLink(value: entry.id) {
Text(entry.title)
}
}
.navigationTitle("Library")
} detail: {
if let entry = entries.first(where: { $0.id == selectedID }) {
Text(entry.title)
.navigationTitle("Reader")
} else {
Text("Choose an article")
}
}
}
}The detail is deliberately just a title so the example isolates navigation. Replace it with the reader without adding a second selected-item property inside that reader. In a real library, use the identifier from the data store, rather than a row position or an identifier regenerated whenever the view updates.
Apple documents that the split view coordinates list selection with the presented detail, including programmatic selection changes. This gives an incoming article link a clear destination: resolve its identifier and update the same selection used by a tap.
The example uses NavigationSplitView, available from iOS 16. Adopting it and rebuilding for iPhone Duo's newest presentation behavior are separate steps. The session calls for Xcode 27.1 and the iOS 27.1 SDK to take advantage of the full inner-display area.
Define what must survive a layout change
Selection is only one part of a reading session. Scroll position, an unfinished annotation, and a presented editor may each need their own state. Decide which values belong to the article, which belong to its presentation, and which must be restored after the app closes.
SwiftUI's state guidance places shared state in the least common ancestor of the views that use it. @State follows the owning view's lifetime; it is not persistent storage. Consequently, avoid tying the navigation root's identity to the size class or placing separate selection owners inside layout branches. Those choices can turn a visual rearrangement into a new state lifetime.
This example provides a single selection owner. It does not implement restoration after termination, synchronize separate scenes, or preserve a reader's scroll position. Add those behaviors explicitly when the product needs them. For an annotation editor, saving the draft should be a model responsibility, independent of which column currently presents it.
Test transitions with an article open
Use the iPhone Duo simulator in Device Hub to open, close, rotate, and fold the device. Start with a selected article, then change the pose. Repeat the reading flow in Split View, where another app changes the space available to yours.
Check the resulting behavior rather than collecting a screenshot of each arrangement:
- The selected article remains the intended detail after the layout changes.
- Navigation back to the library still works when the columns collapse.
- Selecting a different article updates the detail without leaving stale selection behind.
- An annotation draft and any required reading position survive the transitions your app promises to support.
If a transition loses the article, inspect where selection is stored and whether the owning view's identity changes. If the article remains selected but its content no longer fits, inspect the reader's layout. Keeping those responsibilities explicit makes the failure easier to reproduce and the correction smaller.
