The grep Apple’s Tech Talk asks you to run, with the quote attached
iPhone Duo readiness scan
<plist header is a plist, @interface is Objective-C, anything else is Swift.Scanned the built-in sample. 1 file, 30 lines: 11 candidates in live code. Each is a place to read, not a defect.
This is the built-in sample, written to trip most of the patterns. Paste your own code or pick a folder above.
Found in 10 of 16 patterns
Main-screen scale factorApple names thischeck1 live
“Don't reference the main screen on a two-display device — it's ambiguous and will be deprecated.”
Instead: traitCollection.displayScale — the Tech Talk’s own sample replaces UIScreen.main.scale with it.
- (pasted):7:21
let scale = UIScreen.main.scale
Main screen referencedApple names thischeck1 live
“Apple discourages the use of this symbol. Use a UIScreen instance found through context instead. For example, reference the screen that displays a view through the screen property on the window scene managing the window containing the view.”
Instead: The screen of the window scene that holds the view: window?.windowScene?.screen. Marked deprecated in iOS 26.0 on Apple’s own reference page.
A hit already reported by the scale rule is not reported twice.
- (pasted):8:22
let screen = UIScreen.main.bounds
Orientation read for layoutApple names thischeck2 live
“The inner display doesn't honor supported interface orientations, so use size classes rather than orientation for layout decisions.”
Instead: horizontalSizeClass / verticalSizeClass from the SwiftUI environment or the UIKit trait collection. Note Apple deprecates neither orientation API — the guidance is about what to base layout on.
supportedInterfaceOrientations is a different question and has its own rule.
- (pasted):10:12
if UIDevice.current.orientation.isLandscape { - (pasted):10:40
if UIDevice.current.orientation.isLandscape {
One inset reused for the opposite edgeApple names thischeck1 live
“Safe areas and layout margins are often asymmetric on iPhone Duo, so handle each side independently and test in Split View.”
Instead: view.bounds.inset(by: view.safeAreaInsets) — the Tech Talk’s replacement for `bounds.width - safeAreaInsets.left * 2`. Read each edge on its own.
- (pasted):13:47
let usable = view.bounds.width - view.safeAreaInsets.left * 2
Layout branched on device idiomOur pattern, Apple’s quotereview1 live
“The outer display behaves like other iPhone models, while the inner display is regular in both dimensions, leaving room for sidebars.”
Instead: Our inference from that sentence: a `.phone` branch that picks a compact layout will pick it on an inner display Apple describes as regular in both dimensions. Branch on size class. Apple never writes the word "idiom" — the pattern is ours.
- (pasted):14:29
if UIDevice.current.userInterfaceIdiom == .phone {
iPhone-width literal in a frameOur pattern, Apple’s quotereview1 live
“Build your app to resize. Because the device has two displays and supports a wide range of poses and Split View multitasking, your app can appear at many different sizes. Use size classes, layout margins, and safe area insets to lay out controls and content. Avoid fixed widths and display-specific dependencies.”
Instead: A literal from the set of iPhone point widths shipped since 2014. Fine on an icon; on a container it is one idea of how wide a phone is. Apple’s sentence is "avoid fixed widths"; picking these nine numbers is ours.
- (pasted):15:32
collection.frame = CGRect(x: 0, y: 0, width: 390, height: usable)
Safe area ignored on every edgeOur pattern, Apple’s quotereview1 live
“Build your app to resize. Because the device has two displays and supports a wide range of poses and Split View multitasking, your app can appear at many different sizes. Use size classes, layout margins, and safe area insets to lay out controls and content. Avoid fixed widths and display-specific dependencies.”
Instead: Legitimate for a background. For anything interactive, Apple’s HIG lays controls out with safe area insets; on the Duo the side bars, the outer camera and the fold are all safe-area shaped. Which edges, and for what, is the question.
- (pasted):26:13
.ignoresSafeArea()
Hard-coded status-bar or Dynamic Island paddingOur pattern, Apple’s quotereview1 live
“Safe areas and layout margins are often asymmetric on iPhone Duo, so handle each side independently and test in Split View.”
Instead: Read safeAreaInsets.top. Apple has published no status-bar height for either Duo display, so no number can be right here by construction.
- (pasted):27:14
.padding(.top, 47)
Custom ellipsis menuApple names thisreview1 live
“Consolidate any custom overflow into the system menu with ToolbarOverflowMenu or additionalOverflowItems, and reserve the ellipsis for overflow only.”
Instead: ToolbarOverflowMenu (SwiftUI) or navigationItem.additionalOverflowItems (UIKit). Apple asks that the ellipsis be reserved for the system overflow.
- (pasted):17:51
let more = UIBarButtonItem(image: UIImage(systemName: "ellipsis"), style: .plain, target: self, action: #selector(showMore))
Odd number of fixed grid columnsApple names thisreview1 live
“In a grid-style layout, prefer an even number of columns so content divides cleanly.”
Instead: An even count, or adaptive columns. An odd count puts the middle column on the folding region when the device is partially open.
- (pasted):23:19
let columns = Array(repeating: GridItem(.flexible()), count: 3)
Not found in the scanned files — 6 patterns
Listed so you can see what was looked for. “Not found” is a fact about a regex over these files; it is not a pass.
UIRequiresFullScreenApple names thischecknot found
“Opting out of iPad multitasking and dynamic resizing is deprecated. Use a combination of UISceneSizeRestrictions and prefersInterfaceOrientationLocked to replace some of the behaviors of UIRequiresFullScreen.”
Instead: Deprecated in iOS 26.0 and iPadOS 26.0. Apple: handle multitasking and dynamic resizing, then remove the key; UIRequiresFullScreenIgnoredStartingWithVersion names the version from which the system ignores it while you still ship to older iOS.
Not Duo-specific — it arrived with iOS 26 — but a device whose premise is resizing is where an app still opting out of it shows.
Not found in the scanned files. That is a statement about the regex, not about the layout — the same value can live in a constant with another name.
Interface orientations restrictedApple names thisreviewnot found
“Make your game playable in every device pose. You can choose to lock to either portrait or landscape orientation, but be sure to fill the screen as the device pose changes.”
Instead: Locking is allowed. The Tech Talk adds that the inner display does not honor supported interface orientations, so a locked app still has to fill a regular-width, near-square display in every pose.
Not found in the scanned files. That is a statement about the regex, not about the layout — the same value can live in a constant with another name.
Manual spacing between bar itemsApple names thisreviewnot found
“Group related toolbar items instead of spacing them manually. Groups you create with ToolbarItemGroup (SwiftUI) or UIBarButtonItemGroup (UIKit) provide space between items and other groups automatically, and adapt as the available space changes, so avoid adding fixed spacing yourself.”
Instead: ToolbarItemGroup / UIBarButtonItemGroup. The Tech Talk notes flexible spacers are zero size vertically while fixed ones keep their minimum, so a hand-spaced bar lays out differently on the side.
Not found in the scanned files. That is a statement about the regex, not about the layout — the same value can live in a constant with another name.
Bar item with an image and no titleApple names thisreviewnot found
“Provide both a title and a symbol for each toolbar item that isn't text-only. Giving both lets the system pick the right representation for the context. Include a title even when an item shows a symbol, because the system uses the title in overflow menus and expanded forms.”
Instead: init(title:image:primaryAction:menu:). The title is what the system shows when the item overflows into the menu.
The (image:style:target:action:) initialiser has no title parameter at all, which is why it is the one matched.
Not found in the scanned files. That is a statement about the regex, not about the layout — the same value can live in a constant with another name.
System tab bar hiddenApple names thisreviewnot found
“In general, don't override the default bar placement. The position of controls on the vertical axis is one of the core patterns of iPhone Duo.”
Instead: Usually paired with a hand-built tab bar, which will not move to the side or minimise. TabView / UITabBarController receive the vertical layout automatically.
Not found in the scanned files. That is a statement about the regex, not about the layout — the same value can live in a constant with another name.
Face ID assumedApple names thisreviewnot found
“Fingerprint sensor built into the side button”
Instead: Read LAContext.biometryType at runtime and name the sensor from it. The Duo’s specification sheet lists Touch ID in the side button and no Face ID.
Not found in the scanned files. That is a statement about the regex, not about the layout — the same value can live in a constant with another name.
API mentions — counts, not grades
Mentions of the things Apple points at instead. Ten reads of a size class do not make a layout adaptive, and zero reads do not make it fixed — a SwiftUI app on standard containers reads none and adapts anyway. Nothing here enters a score, because there is none.
- Size class reads0 in 0 files
What Apple says to branch on instead of orientation. A count, not a pass.
- Screen read from the window scene0 in 0 files
The Tech Talk’s replacement for the main screen.
- displayScale reads0 in 0 files
The Tech Talk’s replacement for UIScreen.main.scale.
- Reserved-region API mentions0 in 0 files
New in the iOS 27.1 SDK. Apple’s guide for it is listed as "Coming later this month" — a mention is a mention.
- Arrangement view mentions0 in 0 files
iOS 27.1 SDK. The HIG says to keep navigation containers outside it and the Tech Talk says to keep it out of List and ScrollView.
- Hinge API mentions0 in 0 files
Apple’s sample reads a status and an angle. What an app is asked to draw during a partial fold is not documented.
What a regex cannot see
- Whether the layout resizes. Apple’s HIG sentence is “build your app to resize”. That is a property of running code at a regular-width size class, which nothing here does. The simulator Apple names for it is in Xcode 27.1, which two of Apple’s three statements say has not shipped.
- Fixed widths under another name.
let cardWidth = 390is not matched;width: 390is. A constant hides a literal from a pattern and from nobody else. - Storyboards and XIBs. Their fixed constraints are XML this pass does not read. Interface Builder files are skipped, not scanned.
- What the 27.1 APIs do. ArrangementView, ReservedRegion and the hinge status are counted as mentions. Apple’s guide for them is listed as “Coming later this month”, and a symbol name on a talk page is not a specification.
- Anything commercial. Across eleven Apple pages there is no claim that adapting to the Duo moves conversion, ranking or downloads. This page makes none either.
Where the rules come from
Every quote was fetched from Apple and read on 2026-09-13, not recalled. The Tech Talk’s own summary is the spine of the list:
“Your app runs on iPhone Duo even without recompiling, but screen usage improves with each SDK.”
Two things are worth knowing that the rows do not shout. Apple has deprecated the main screen (iOS 26.0 on its own reference page) and UIRequiresFullScreen (iOS 26.0), but has deprecated neither orientation API — that guidance is about what to base layout on, not a removed symbol. And Apple names no capability key, device family or plist declaration for the Duo: the SDK you build against is the only lever it documents.
Signals counted: 6. Files over 2 MB and scans over 3,000 files are truncated and reported as skipped, never silently dropped.
About this tool
How do you check an iOS app for iPhone Duo before the simulator exists?
Apple’s Tech Talk for the Duo names the code that behaves differently on a two-display phone: referencing the main screen, deciding layout from orientation, assuming the left and right safe-area insets match, and opting out of resizing with UIRequiresFullScreen. Each is the kind of code that keeps working on every other iPhone and shows on this one.
This page is that list as a scan. Paste a file or pick your project folder; every Swift, Objective-C and plist file is read in your browser and matched against the patterns, and each hit is shown with Apple’s sentence about it and what Apple names as the replacement. Nothing is uploaded, nothing is stored, and there is no score.
How it works
- 1Paste code, or choose files or a folder — or drop either onto the box. Only .swift, .m, .mm, .h and .plist files are opened; dependency and build directories are skipped and listed.
- 2Each finding names the file, line and column, shows the line, and says whether it sits inside a comment. A commented-out call is reported — the pattern is there — but never counted with the live ones.
- 3Each pattern is labelled either "Apple names this" (the main screen, orientation, insets, UIRequiresFullScreen, custom overflow menus, symbol-only bar items, odd grid columns, manual bar spacing) or "our pattern, Apple’s quote" (idiom branches, fixed iPhone widths, blanket safe-area ignores, hard-coded top padding), so you can see whose inference each row is.
- 4Patterns that did not match are listed as "not found", never as passed. Mentions of the APIs Apple points at instead are counted, not graded.
- 5Copy the report as Markdown, or export CSV or JSON, for a ticket or a pull request.
What it cannot tell you
- A finding is a place to read, not a defect. A fixed width on an icon is fine; a fixed width on a container is not; a regex cannot tell them apart and does not try.
- Zero findings is not readiness. It cannot see whether the layout resizes, which is the thing Apple actually asks for, and it cannot see a literal hidden behind a constant with another name.
- Storyboards and XIBs are not read. Their fixed constraints are XML this pass does not parse.
- It counts mentions of ArrangementView, ReservedRegion and the hinge APIs without judging them. Apple’s guide for those is listed as coming, and a symbol name on a talk page is not a specification.
- It makes no claim about downloads, ranking or conversion. Apple makes none about the Duo either.
Questions
Is my code uploaded?
No. Files are read with the browser’s own file API inside this tab and matched there. The page makes no network request while scanning, keeps nothing in storage, and the report exists only until you leave.
Why is UIScreen.main flagged?
Apple’s Tech Talk says "Don’t reference the main screen on a two-display device — it’s ambiguous and will be deprecated", and its reference page marks UIScreen.main deprecated in iOS 26.0. The replacements Apple names are the window scene’s screen and traitCollection.displayScale.
Why is orientation flagged when Apple has not deprecated it?
Because the guidance is about what to base layout on, not a removed symbol. Apple states the inner display doesn’t honor supported interface orientations and says to use size classes for layout decisions. UIDevice.orientation itself is not deprecated, and the row says so.
What does "our pattern, Apple’s quote" mean?
That Apple wrote the sentence the row rests on but never named the code pattern. Apple says the inner display is regular in both dimensions and says to branch on size class; it never writes the word "idiom". The userInterfaceIdiom rule is therefore ours, and labelled as such.
Compliance & technical
Or browse every free App Store optimization tool.