Sitemap

Your UIKit App Will Run on iPhone Duo. Will It Look Right?

19 min readOct 20, 2026

--

iPhone Duo

Apple says iPhone Duo will be available to customers starting October 23, 2026. I recently prepared a UIKit app at my company for this device. While working on the migration, I collected notes about the areas that needed attention. Some problems were easy to find. Others needed more research in Apple’s documentation and more testing.

I turned these notes into a practical guide. It is not a complete list of every possible problem. It covers the common areas that every UIKit project should review before moving to the new SDK.

I also used AI tools to organize information and explore ideas, but I treated them as helpers, not as the final source of truth. For technical decisions, I checked Apple’s documentation.

The main lesson is simple: do not treat this migration as only a screen-size problem. App lifecycle, layout, system bars, animations, third-party SDKs, and testing are also important.

Before You Start: The Five Most Important Things

If you have little time, start with these five tasks:

  1. Move to the scene-based lifecycle. Apps built with the iOS 27 SDK must use it.
  2. Search for UIScreen.main and other fixed screen sizes. Use the size of the view that owns the content instead.
  3. Test standard navigation and tab bars with the iOS 27.1 SDK. Decide how your custom bars should behave.
  4. Check collection views, custom animations, and camera layers while the window changes size.
  5. Test with the iPhone Duo simulator and prepare your App Store screenshots early.

This guide is for iOS developers who maintain UIKit apps in Swift or Objective-C and need to prepare for the iOS 27 and iOS 27.1 SDKs. The testing and release sections are also useful for SwiftUI developers.

For the official overview, start with Apple’s Prepare your app for iPhone Duo guide.

1. Understand What Changes on iPhone Duo

On iPhone Duo, the app window can change between the outer display and the inner display. The available space changes, but your existing view controllers do not necessarily start from zero each time.

This means that a value you calculated once can become wrong after the window changes size.

For example, a view may look correct on the outer display but appear too wide, too small, or in the wrong place on the inner display.

Apple also explains that an iPhone app can now have different combinations of size classes. So you should stop thinking that an iPhone always means compact width.

The good news is that your app can run on iPhone Duo even if you do not rebuild it. However, the experience improves with the newer SDKs.

  • Older SDKs: The app can run, but it may not use all of the inner display. It can still resize when the device opens or closes.
  • iOS 27 SDK (Xcode 27): Apps must use the UIKit scene-based lifecycle.
  • iOS 27.1 SDK (Xcode 27.1): Apps can take advantage of the newer iPhone Duo experience, including the full inner display and vertical system bars where supported.

One more detail: iPhone Duo still honors the UIRequiresFullScreen key, but your app can still resize when someone opens or closes the device. This key does not protect you from layout problems.

Before changing any code, check three areas:

  • Adaptive layout: Can the UI work with different window sizes?
  • App lifecycle: Does the app use the scene-based lifecycle?
  • System UI: Do navigation bars, tab bars, safe areas, and custom controls work correctly?

Do not treat every SDK version as the same requirement. Check Apple’s documentation for the exact behavior of the SDK you use.

2. Start with the Current App

I would not start by changing code.

First, build the existing app and run it in the iPhone Duo simulator without migration changes. This gives you a baseline.

Check the outer display, the inner display, different orientations, and the folding and unfolding process. Take screenshots of broken screens. These screenshots will help you create a clear task list.

Apple’s iPhone Duo preparation guide is the best starting point for the supported tools and testing workflow.

If your development environment provides an AI coding assistant with an app-resizability scan, you can use it to find common layout problems. Treat the result as a first scan, not as proof that the app is ready. You still need to run the app and inspect the screens.

Check third-party SDKs early. A chat view, camera screen, banner, or custom sheet may have its own layout code. Fixing your main app does not automatically fix these parts.

Keep the work in small steps. For example, scene lifecycle, screen size, system bars, collection views, and media can be separate tasks or pull requests. Small changes are easier to review and test.

3. Review the UIKit Scene Lifecycle

This is one of the first things I would check.

Apps built with the iOS 27 SDK must use the UIKit scene-based lifecycle. If an app does not meet this requirement, it will fail to launch. Apps built with the iOS 26 SDK are not affected by this particular rule, but you need to move to the scene-based lifecycle before building with the newer SDK.

You do not need to support multiple windows. A single scene can be enough.

Start with the app’s Info.plist and scene configuration. If you create the window in code, connect it to a UIWindowScene:

func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = scene as? UIWindowScene else {
return
}

let window = UIWindow(windowScene: windowScene)
self.window = window
// Configure the root view controller.
// Start the app's coordinator.
window.makeKeyAndVisible()
}

This is a basic example. The exact setup depends on your app’s architecture and coordinator.

Before migrating, review these parts of your project:

  • Add a valid UIApplicationSceneManifest configuration to the app's Info.plist, or provide the configuration from application(_:configurationForConnecting:options:).
  • Make sure UIKit can find your SceneDelegate class.
  • Create the main window using the connected UIWindowScene.
  • Move scene-specific UI lifecycle work to SceneDelegate. Keep app-wide setup in AppDelegate.
  • Update your coordinator so it uses the scene’s window instead of assuming that the app has one global window.
  • Test cold launches, background and foreground transitions, and incoming URLs.

A single scene is enough for apps that do not need multiple windows. Adopting the scene-based lifecycle does not automatically mean that your app must support multiple windows.

Avoid creating the main window with a fixed screen size:

UIWindow(frame: UIScreen.main.bounds)

Create it with the correct window scene instead.

Also review what AppDelegate and SceneDelegate do. App-wide setup, such as third-party SDK configuration, can stay in AppDelegate. Window setup and scene-specific UI work belong in the scene lifecycle.

For the official migration steps, read Apple’s technical note: TN3187: Migrating to the UIKit scene-based life cycle.

Do Not Forget Deep Links

Moving to scenes can change how your app handles external links. Test these cases separately:

  • URL schemes, such as myapp://product/123
  • Universal links
  • Push notification taps
  • Home screen shortcuts
  • Spotlight or Handoff activities, if your app uses them

There is a big difference between a cold launch and an app that is already running. When the app starts from a link, the information arrives in scene(_:willConnectTo:options:). When the scene is already running, other scene delegate methods receive it, such as scene(_:openURLContexts:) and scene(_:continue:).

Make sure the link is not lost while the root screen, login flow, or coordinator is still starting.

My recommendation is to send these entry points to one routing layer. This makes the behavior easier to maintain and test.

Here is a simple example. The router keeps a link until the app is ready:

@MainActor
final class DeepLinkRouter {
private var pendingURL: URL?
private var isReady = false

func handle(_ url: URL) {
guard isReady else {
pendingURL = url
return
}
route(to: url)
}
func markReady() {
isReady = true
guard let url = pendingURL else {
return
}
pendingURL = nil
route(to: url)
}
private func route(to url: URL) {
// Parse the URL and tell the coordinator where to go.
}
}

This is only a starting point. If multiple links can arrive before the app becomes ready, a single pendingURL can overwrite an earlier link. A production implementation should define how multiple incoming routes are handled.

Also remember to process every relevant incoming URL or user activity, rather than assuming that only one can arrive. Push notification taps and shortcut items can use the same routing layer after being converted into an app route.

4. Remove Fixed Screen-Size Assumptions

One of the most useful searches in an existing UIKit project is:

grep -rn "UIScreen.main\|UIScreen.screens" \
--include="*.swift" .

Also search your local packages and forked SDKs. You may find old code that still assumes the screen size is the same as the size available to a view.

Here are a few more searches I would run. Some include Objective-C patterns because older code often hides there.

Fixed screen sizes

grep -rnE "UIScreen.main|UIScreen mainScreen" \
--include="*.swift" --include="*.m" .

Windows created with a fixed frame

grep -rnE "UIWindow\(frame|initWithFrame:\[UIScreen" \
--include="*.swift" --include="*.m" .

Orientation and idiom checks

grep -rnE "statusBarOrientation|UIDevice.current.orientation|userInterfaceIdiom" \
--include="*.swift" --include="*.m" .

Casts to the flow layout

grep -rnE "as[?!] UICollectionViewFlowLayout" \
--include="*.swift" .

Layers that need manual frame updates

grep -rnE "AVCaptureVideoPreviewLayer|AVPlayerLayer" \
--include="*.swift" --include="*.m" .

These searches only give you a list of places to review. Not every match is a bug.

The real problem is the assumption that the screen size is the same as the size available to a view.

For example, this calculation assumes that both sides have the same inset:

let width = view.bounds.width
- view.safeAreaInsets.left * 2

This can be wrong when the left and right safe area insets are different.

Use the real insets instead:

let contentWidth = view.bounds
.inset(by: view.safeAreaInsets)
.width

For layout calculations, use the size of the view or window that owns the content. Also look for global values such as these:

let screenWidth = UIScreen.main.bounds.width
let screenHeight = UIScreen.main.bounds.height

If you calculate these values once and store them, they can become outdated when the window changes size.

Move size-dependent work to an appropriate layout update point, such as layoutSubviews() or viewDidLayoutSubviews(). Use transition callbacks when you need to react to a specific size change. The right place depends on how the view is built, so do not move every calculation into the same method without checking what it does.

For example, a view controller can choose its layout based on the width it actually has:

private enum LayoutMode {
case compact
case wide
}

private var layoutMode: LayoutMode = .compact

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let availableWidth = view.bounds
.inset(by: view.safeAreaInsets)
.width
// Use a breakpoint from your own design.
let newMode: LayoutMode =
availableWidth >= 700 ? .wide : .compact
guard newMode != layoutMode else {
return
}
layoutMode = newMode
updateLayout(for: newMode)
}

The guard is important. It stops the screen from rebuilding its layout on every small change.

For more details, see Apple’s Prepare your app for iPhone Duo guide.

5. Review Orientation and Size Class Logic

The orientation of a device does not always tell you how much space your app can use.

Search for code that uses:

  • UIDevice.current.orientation
  • statusBarOrientation
  • interfaceOrientation
  • isLandscape and isPortrait
  • userInterfaceIdiom == .pad

These checks are not always wrong. But you should not use them as a shortcut to decide how a layout should look.

For example, if your app shows the iPad layout only when userInterfaceIdiom == .pad, the wide layout may never appear on a wide iPhone Duo window.

Review your size class logic too. A regular horizontal size class does not always mean the user is on an iPad. A wide iPhone window can also have regular width.

A better question is: How much space does this view have right now?

Check the real view size and the size classes, then choose the layout that fits.

Also review screens that support only portrait. Your app’s supported orientations still matter, but the inner display and Split View can create short or wide windows that reveal layout problems.

6. Check System Bars and the System Look

System bars need special attention on iPhone Duo.

With the iOS 27.1 SDK, standard UIKit bars can move to the side and become vertical. Apple’s guidance is to rebuild with the newer SDK and use the bars provided by UIKit navigation containers where possible.

Custom bars do not get this behavior automatically. A custom tab bar or toolbar may need its own layout and interaction decisions.

If your app uses a standard UINavigationController and UITabBarController, test them before replacing anything. If your app has a custom navigation bar or tab bar, decide what it should do on the inner display. This is a design decision as well as a technical one.

For screens where a vertical bar does not work well, UIKit provides preferredVerticalBarBehavior:

override var preferredVerticalBarBehavior: UIVerticalBarBehavior {
.disabled
}

Check the current preferredVerticalBarBehavior documentation before adopting this API, especially when using a beta SDK.

Do not add this to one view controller and assume that it covers the whole app. Navigation controllers, tab bar controllers, presented screens, and third-party view controllers may need separate checks. Test the behavior in your real navigation hierarchy.

Check Custom Tab Bars

A custom tab bar may disappear or sit in the wrong place when the available width changes. Test it on both displays and while folding and unfolding the device.

Also check how your app looks with the system’s regular-width tab bar layout. A design that works on a narrow iPhone may not work in a wider window.

Check Contrast and Visual Style

Do not only check if the app launches. Look at the contrast between text, icons, backgrounds, and system bars.

Custom shadows, gradients, and buttons can look different when bars move or when content goes under system areas.

Some components will work well with the system style. Others may need a new design. Talk to your design team early. If you wait, you may need to repeat engineering work after the visual direction changes.

Apple’s Raise the bar with iPhone Duo Tech Talk is a useful reference for this area.

7. Review Safe Areas and Layout Constraints

Safe areas are another important part of this migration.

On the inner display, the left and right safe area insets may not be equal. System bars and other reserved areas can create a different amount of space on each side.

Avoid calculations that assume the same inset on both sides.

Review your constraints and decide if each view should be attached to the safe area or to the full view bounds.

For example:

  • Buttons and form controls should stay in usable areas.
  • Background colors and background images may need to cover the full view.
  • Sheets, alerts, and popups should not have strange empty space on one side.
  • Custom views should react when the available width changes.

Do not fix every layout problem by adding a new fixed constant. That can hide the problem on one display and create a new problem on the other.

For advanced adaptive-layout scenarios, see Apple’s Strike a pose with adaptive layouts on iPhone Duo.

8. Review Collection Views and Custom Layouts

Collection views need their own review. Many apps use them for product lists, banners, stories, grids, and paged content.

“Start by searching for layout code that depends on a fixed screen width. For example, sizeForItemAt may use a global screen width to calculate cell sizes. These calculations should use the real collection view size or its layout environment.

For complex layouts, UICollectionViewCompositionalLayout can be a good choice because sections can adapt to the available width. But moving to it is not a simple replacement.

Here is a small example. It chooses the number of columns from the width of the collection view, not from the screen:

func makeLayout() -> UICollectionViewLayout {
UICollectionViewCompositionalLayout { _, environment in
let width = environment.container.effectiveContentSize.width
let columns = max(2, Int(width / 180))
let fraction = 1.0 / CGFloat(columns)

let item = NSCollectionLayoutItem(
layoutSize: .init(
widthDimension: .fractionalWidth(fraction),
heightDimension: .estimated(220)
)
)
let group = NSCollectionLayoutGroup.horizontal(
layoutSize: .init(
widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(220)
),
subitems: [item]
)
return NSCollectionLayoutSection(group: group)
}
}

UIKit can recalculate the section layout when the layout is invalidated, including when the collection view’s size changes. This allows the column count to follow the available width.

Before you replace UICollectionViewFlowLayout, search for code that casts the layout to UICollectionViewFlowLayout. A normal cast (as?) returns nil after the change, so some behavior may silently stop working. A forced cast (as!) can crash the app.

Get Okan Orkun’s stories in your inbox

Join Medium for free to get updates from this writer.

Also check:

  • Paging offsets after folding and unfolding
  • The position of the current item
  • Section insets and spacing
  • Self-sizing cells
  • Layout invalidation when the size changes
  • Custom transition animations

A list may look correct on the outer display but show the wrong item after the window becomes wider. Do not test only the first layout. Scroll, change the window size, and check the result again.

9. Check XIB Files, Custom Views, and Animations

Not every layout problem comes from Auto Layout constraints.

Some XIB files have views with fixed sizes or incorrect autoresizing masks. In collection view cells, check that the contentView can resize with the cell. A cell with an old fixed width may look fine at first and then be clipped after the window changes size.

Custom drawing also needs attention. Review views that draw into their bounds, use CALayer, or create their own animations. Make sure frames, paths, and layer sizes update when the view size changes.

For example, a custom progress bar may animate to a fixed width. If the window becomes wider during the animation, the progress can stop at the wrong place.

A better approach is to keep the current progress value and calculate the target width from the current bounds:

final class ProgressBarView: UIView {
private let fillView = UIView()
private(set) var progress: CGFloat = 0

// Add fillView as a subview in your initializer.
func setProgress(_ value: CGFloat, animated: Bool) {
progress = min(max(value, 0), 1)
setNeedsLayout()
guard animated else {
return
}
UIView.animate(withDuration: 0.3) {
self.layoutIfNeeded()
}
}
override func layoutSubviews() {
super.layoutSubviews()
fillView.frame = CGRect(
x: 0,
y: 0,
width: bounds.width * progress,
height: bounds.height
)
}
}

Now the fill width follows the view’s current bounds after resizing.

The same idea applies to custom page transitions. If a transition uses a 3D transform or a fixed translation value, it may use an old size after folding or unfolding.

Do not change these animations only because of how the code looks. Reproduce the problem, check the layout and animation values, and test your fix.

10. Review Forms and Full-Screen Screens

Forms and onboarding screens are easy to forget.

A screen may fit on the outer display but become too short on the inner display in a wide orientation.

Check screens with:

  • Long forms
  • Many buttons
  • Large illustrations
  • Login and registration flows
  • Onboarding steps
  • Full-screen content

If the content does not fit in the available height, use a scroll view. With Auto Layout, connect the content to the scroll view’s contentLayoutGuide. For vertical scrolling, connect the content width to the frameLayoutGuide width.

Do not just make fonts smaller or buttons shorter to fit everything. The screen must stay usable.

Also review large images. An image with a fixed aspect ratio can become too big in a wider window. Set sensible size limits, and decide if the image should use aspect fill or aspect fit.

11. Check Modals and Third-Party SDKs

Some of the hardest problems appear in screens that are not part of your main view controller hierarchy.

Review full-screen presentations, sheets, alerts, pickers, and third-party screens.

Pay attention to the presentation style. With .fullScreen, UIKit removes the presenting view from the view hierarchy, and its appearance callbacks run again when you come back. With .overFullScreen, the presenting view stays in place and those callbacks do not run in the same way.

If you change the presentation style, check where your app refreshes its content.

Test sheets while the window changes size. Use the sheet presentation and detent APIs instead of fixed heights.

For third-party SDKs, check if you can read the source code. If the SDK is open source or you have a fork, search it for fixed screen sizes, custom transitions, and layout assumptions.

If you cannot change the source, contact the provider and test the SDK in your target environment.

The migration is not finished just because your own screens look correct.

12. Review Camera and Media Screens

Camera previews, video players, and full-screen images can show problems that normal screens do not.

Check if the camera orientation is hard-coded. If your minimum iOS version allows it, review AVCaptureDevice.RotationCoordinator (iOS 17 and later) and your current camera orientation approach.

Also check AVPlayerLayer and AVCaptureVideoPreviewLayer. They are layers, so Auto Layout does not size them like normal views. You may need to update their frames during layout.

For example, a view can own the preview layer and update its frame when it lays out:

import AVFoundation

final class CameraPreviewView: UIView {
let previewLayer = AVCaptureVideoPreviewLayer()
override init(frame: CGRect) {
super.init(frame: frame)
layer.addSublayer(previewLayer)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
layer.addSublayer(previewLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
previewLayer.frame = bounds
}
}

Because the view follows the window size, the layer follows it too.

Check the crop of images and videos. An image with .scaleAspectFill can lose important content when the available area changes. Test different aspect ratios, and decide if the content should fill the space, fit inside it, or use a focus point.

Finally, review image downsampling. If you prepare an image for a small view and later show it in a much bigger view, it can look blurry. Check if the image should be loaded again at a larger size.

For related platform guidance, start with Apple’s iPhone Duo developer resources.

13. Test More Than the First Launch

I would not call the migration complete after a successful build.

Test different sizes and states. This is the test list I use:

  • Launch the app on the outer display.
  • Launch the app on the inner display.
  • Test portrait and landscape layouts.
  • Fold and unfold the device while the app is open.
  • Test with a sheet, alert, or full-screen screen visible.
  • Test the app in Split View.
  • Test deep links and universal links with the app closed and open.
  • Test push notification taps and home screen shortcuts.
  • Check collection view paging after resizing.
  • Check custom tab bars and navigation bars.
  • Check camera previews and full-screen media.
  • Test the app on a regular iPhone with iOS 27.
  • Test the app on your minimum supported iOS version.
  • Check that third-party SDKs still work.
  • Check the layout at very narrow and very wide sizes.
  • Use the supported simulator and device tools described in Apple’s iPhone Duo preparation guide.

When you find a problem, save a screenshot or a recording. Add logs if you need them. A clear way to reproduce a bug is much more useful than a guess.

14. Use AI as a Helper, Not as the Final Reviewer

AI tools can help with migration work, especially when the task is clear and small.

For example, AI can find UIScreen.main usage, review scene lifecycle code, suggest constraint changes, or explain a collection view layout.

But AI cannot confirm that a screen looks correct just by reading code.

I recommend this workflow:

  1. Ask AI to scan one part of the app.
  2. Ask it to list findings with file names and line numbers.
  3. Review the findings before changing anything.
  4. Ask for a small, focused fix.
  5. Build and test the app.
  6. Share the result, logs, or screenshots if there is still a problem.
  7. Do not give the whole migration to AI in one big task. Split it into small steps and review every change.

And do not let AI make design decisions for you. Whether to keep a custom tab bar, change a presentation style, or redesign a wide screen is a decision for the development and design teams.

AI can help you move faster, but testing is still your job.

15. Prepare for App Store Submission

This part is separate from making the app work correctly.

Apple’s iPhone Duo developer announcements and App Store Connect documentation explain the submission requirements and screenshot workflow. Check the current details in Apple’s developer resources and App Store Connect screenshot specifications.

Before release:

  • Build and test with the correct SDK.
  • Check the app in different device poses and orientations.
  • Prepare the required screenshots.
  • Review the product page in App Store Connect.
  • Check that the app still works on supported older iOS versions.
  • Do not leave screenshots and release checks for the last day.

Quick Questions and Answers

1- Will my app run on iPhone Duo if I do not rebuild it?

Apps can run without being rebuilt, but they may not use the available display space as effectively as apps prepared with the newer SDKs. Check Apple’s preparation guide for the current behavior.

2- Do I need to support multiple windows to adopt scenes?

No. Using the scene-based lifecycle does not mean your app must support multiple windows. A single scene can be enough.

3- My app is built with the iOS 26 SDK. Will it fail to launch on iOS 27?

The launch requirement described here applies to apps built with the iOS 27 SDK. You should still test compatibility with the OS versions you support.

4- Is UIRequiresFullScreen enough to avoid the work?

No. Do not rely on this key to prevent layout problems when the device opens or closes. Test your app at the sizes it can actually use.

5- When do I need iPhone Duo screenshots?

Check Apple’s latest App Store Connect screenshot specifications before submitting. Submission requirements can change, so use the current official guidance rather than relying only on an article.

Final Checklist

Before you call the migration complete, review these areas in every UIKit project.

App lifecycle

  • The scene manifest and SceneDelegate are configured.
  • The main window uses UIWindow(windowScene:).
  • Cold and warm deep links work.
  • Push notification taps and shortcuts work.

Screen size and layout

  • Fixed screen-size assumptions have been reviewed.
  • Global width and height values are not used for adaptive layout.
  • Orientation and idiom checks have been reviewed.
  • Safe area insets are handled separately on each side.
  • Forms and onboarding screens work in short windows.

System UI

  • Navigation and tab bars work on both displays.
  • Custom bars have a clear behavior on the inner display.
  • Contrast and visual style have been checked.
  • Sheets, alerts, and popups have no strange gaps.

Custom components

  • Collection view layouts update correctly.
  • Paging stays correct after resizing.
  • XIB cell content views can resize.
  • Custom views and layers update their sizes.
  • Animations do not depend on old dimensions.
  • Third-party SDKs have been reviewed.

Testing and release

  • Different poses and orientations have been tested.
  • Folding and unfolding has been tested while the app is running.
  • Split View has been tested.
  • Minimum supported iOS versions have been tested.
  • App Store screenshots and release requirements have been reviewed.

Final Thoughts

For me, the most important lesson is that a device migration is not only about making the UI fit a new screen. It is about finding assumptions that were safe before but may not be safe now.

A fixed screen width, an old lifecycle method, a custom tab bar, or a small animation detail can become a real problem when the available space changes.

My approach is simple: scan first, understand the reason, make small changes, and test every step.

This checklist comes from my migration notes and research. Some solutions depend on your app’s architecture, and SDK behavior can change, so always check Apple’s latest documentation.

I hope this guide helps other iOS developers prepare their UIKit apps for iPhone Duo. If you found other migration problems or better solutions, I would be happy to learn from you.

This guide does not cover everything. For example, I did not go deep into multiple scenes, apps that use both displays at the same time, or camera design. Apple’s iPhone Duo developer video collection is a good next step.

--

--