M
Maksim Zhelezniakov
Guest
It’s a new era in the Apple ecosystem — iPhone Duo is here! By the time you’re reading this, it might have become available in a store near you. I’m sure it’s exciting for anyone to at least hold it in their hands and check out the animation that they talk about online. But what was more important and sometimes stressful for me as an iOS developer is figuring out how to adapt my apps to this new form factor — something like this happens very rarely in the Apple world. Many of us tend to ignore iPad app versions, especially as an indie dev. We thought that not every app needs to exist on tablet-like devices, so why waste dev time on it? Frankly, I did the same with my own app Oysia back in the day. Now with the iPhone Duo the rules are changing — developers need to think about app resizing, and how it looks and works on the Duo. Apple cares about its user experience so much that from April 2027 every upload to the App Store Connect has to be built with the iOS 27 SDK, and every app built with the 27.1 SDK has to ship with Duo screenshots. So yes, you need to start exploring Duo adaptation now, not when it’s too late.
My app is a city guide localized into 17 languages. It shows recommended places to visit around the world, mostly cafes and restaurants. When I started the Duo work, it supported only a portrait layout on regular iPhone models. Two weeks and 10 commits later, I released the Duo-compatible version on October 8 using only the new simulator as the testing ground. In this article I’m going to talk about the new APIs Apple provides for the Duo and all the non-obvious challenges I worked through while adapting the app for the new device model.
First of all, there is a split in iOS SDK versions: everything older than iOS 27 runs in a phone-sized window. That’s why you’re going to see a lot of apps at first that show up as a small iPhone version with black areas around the scene on the inner display. iOS 27.0 SDK will stretch out your app scenes to fill the main display area, but it doesn’t “know” about the vertical bar on the right edge where all toolbar buttons normally go on the Duo. And finally, the one and only version you need to support the Duo is iOS 27.1 and later — that’s the one I used, shipped with Xcode 27.1. Below you can see examples of how my app looks by default in all 3 cases:
Now let’s briefly go through all the Duo APIs Apple provides:
The Duo has many different poses. It can be half bent and used as a mini laptop. You’ve probably seen online the Netflix app example of how it can be utilized. Apple’s HIG has a separate detailed page on the design concepts behind the device and first-party best practices for developing apps for it. I recommend reading it first before jumping on the bandwagon and adapting your applications. Here I’m going to list some gotchas I’ve noticed while playing with the new Duo simulator that ships with Xcode 27.1:
The last piece still looks controversial to me, and I wouldn’t mind if Apple could provide some way to customise this in future SDK updates. In my Oysia app I decided not to use
I tested many layout options for my app screens before finally deciding which ones I’m going to keep. For me the UI part is important, and the default Apple way doesn’t always land where I want it. It really depends on your app’s look and its flows. I have lists of cities and places in Oysia, so the natural solution on the Duo was to split the lists into two columns. The main question was how? If I put it into the default
The problem can be solved with padding, but I felt like it was a clunky solution: you need to consider whether a column you show is the left or right one and apply different padding on each side so it looks even. Instead, I decided to track the crease and make the cells the same width, as in the picture below:
Below is the code helper snippet that allowed me to achieve this goal. Based on it I prepared a generic view with a geometry reader that takes both parts on each side of the crease and lays them out in the way I want. The nice thing about this custom layout is that the content does not change if you half-bend the Duo — it already considers the crease.
The second layout kind I developed is used in a place view. It shows a swipeable gallery of pictures on the left side and the place information on the right side. Here I also reused a crease reader and the helpers above:
In onboarding screens and registration flows I used the third type of layout where I put the graphics on one side and the information on the other:
Now let’s go over some unexpected bugs that I found during the testing and fixed along the way:
Make sure that you test all the different combinations of Duo shapes and states of your screens thoroughly. It’s very easy to miss bugs and unexpected behaviors, even if you decide to adapt your app with minimal changes without changing the UI much.
You need to use Xcode 27.1 and later to submit your app update to the App Store. When Apple opened Duo submissions, they also changed the way you deal with screenshots quite a lot. Per-version screenshot sets are gone, and now all of the app’s assets get uploaded into the Asset Library, not only the screenshots and video previews, but also creative assets for page headers, search results and in-app events.
Apple didn’t stop there, and they announced that from April 2027, any app you upload to App Store Connect has to be built with the iOS 27 SDK or later. And any app built with the iOS 27.1 SDK or later has to include iPhone Duo screenshots. This is the Apple way, so we as developers are required to think about the new form factor in advance.
My open source tool appshot-studio prepares localized versions of App Store screenshots with device frames, including iPhone Duo. Check it out, if you’re interested!
Start by running your app in the new Duo simulator and see how it works without any changes at first. Prepare an adaptation plan for your screens, and go one by one carefully testing the flows. For those of you working for companies, start pushing your design teams now. You need to work together in this process and try to get the Duo version out before the deadline. I hope the experience I’ve shared here helps you achieve this challenging goal.
My app is a city guide localized into 17 languages. It shows recommended places to visit around the world, mostly cafes and restaurants. When I started the Duo work, it supported only a portrait layout on regular iPhone models. Two weeks and 10 commits later, I released the Duo-compatible version on October 8 using only the new simulator as the testing ground. In this article I’m going to talk about the new APIs Apple provides for the Duo and all the non-obvious challenges I worked through while adapting the app for the new device model.
What Apple gives you for the Duo
First of all, there is a split in iOS SDK versions: everything older than iOS 27 runs in a phone-sized window. That’s why you’re going to see a lot of apps at first that show up as a small iPhone version with black areas around the scene on the inner display. iOS 27.0 SDK will stretch out your app scenes to fill the main display area, but it doesn’t “know” about the vertical bar on the right edge where all toolbar buttons normally go on the Duo. And finally, the one and only version you need to support the Duo is iOS 27.1 and later — that’s the one I used, shipped with Xcode 27.1. Below you can see examples of how my app looks by default in all 3 cases:
Now let’s briefly go through all the Duo APIs Apple provides:
Reserved Regions
This new type refers to some areas of an iPhone display that are reserved by the device’s hardware or software. Currently we have only two kinds of such areas:occlusionfor the camera and the Dynamic Island anddivisionfor the hinge area where our content gets split into parts. In SwiftUI these regions are provided byGeometryProxyinside anyGeometryReader. The idea is to avoid content overlapping occlusion areas and split the content at division regions when they’re active. We’re going to talk about it more later on.
Arrangement View
It’s a container view that automatically lays out two views, primary and secondary, either side by side or overlaid. At least for now I cannot imagine how I would use the overlay style. As you can guess, if you close the Duo, theArrangementViewwill only show your primary content on the outer display. Think of it as a new SwiftUI version ofUISplitViewControllerfor the iPhone Duo.
Vertical toolbar
By default all the controls that were usually placed on the top and the bottom of your iPhone screen will get moved to the right edge of the Duo inside a vertical toolbar on the outer display and on the inner display when held wide. It applies only to system bars in aNavigationStackorTabView, custom bars are ignored. The tab bar will appear on the bottom of it, while other toolbar items, like a back button, will appear on the top. You can customize this behavior with a bunch of SwiftUI modifiers — for example,.axisBehavior(.verticalPreferred)marks aToolbarItemas preferring the vertical bar.toolbarVerticalBehavior(.disabled)will turn off the vertical bar, whiletoolbarVerticalCompressionBehaviorlets you specify the toolbar behavior when there is not enough space.
DeviceHinge & DeviceHingeContext
These types are provided by the onHingeChange(isEnabled:_
modifier and allow you to react to the changes of the hinge context. It’s best used for animations and effects when you fold and unfold the Duo. For example, you can apply a complex dissolving effect to your views when the hinge state changes.
How your app sees the iPhone Duo
The Duo has many different poses. It can be half bent and used as a mini laptop. You’ve probably seen online the Netflix app example of how it can be utilized. Apple’s HIG has a separate detailed page on the design concepts behind the device and first-party best practices for developing apps for it. I recommend reading it first before jumping on the bandwagon and adapting your applications. Here I’m going to list some gotchas I’ve noticed while playing with the new Duo simulator that ships with Xcode 27.1:
- The inner display has dimensions of 951 x 669 in points when held open wide (both size classes are regular), the crease runs at 455.5–495.5, so it is 40 pt wide. The outer display is 466 x 678 pt with a compact width size class. Note: these are measured on the simulator, but I don’t expect it to be different on a real device.
- The inner display ignores the app's supported orientations. It’s expected, Apple said that in their tech talk 111461 "Prepare your app for iPhone Duo". The only exception is an app with
UIRequiresFullScreenthat keeps its orientations but scales on the inner display. - The crease can be found with the reserved regions API mentioned above. The most interesting part is that the region can be active and inactive. By default, when you hold the Duo open and flat, the crease returns as inactive using
options: [.includeInactive]. What does it mean in practice? Apple’s ownArrangementViewcompletely ignores it in a full open state, meaning the content will get into this area and treat the whole display real estate as uninterrupted. However, when you half-bend the device, the crease becomes active, and the content will move right and left to avoid it. You can see this behavior in Apple’s own apps.
The last piece still looks controversial to me, and I wouldn’t mind if Apple could provide some way to customise this in future SDK updates. In my Oysia app I decided not to use
ArrangementView, I’m going to explain why and what I decided to do instead below.Three layouts that covered every screen for me
I tested many layout options for my app screens before finally deciding which ones I’m going to keep. For me the UI part is important, and the default Apple way doesn’t always land where I want it. It really depends on your app’s look and its flows. I have lists of cities and places in Oysia, so the natural solution on the Duo was to split the lists into two columns. The main question was how? If I put it into the default
ArrangementView, the cells would stretch out too much, and there wouldn’t be enough breathing space for them to exist. In my opinion, it won’t look good. Below you can see an example where the left column stretches to the leading edge of the screen. Imagine the column also running into the crease: the cells would stretch even more.
The problem can be solved with padding, but I felt like it was a clunky solution: you need to consider whether a column you show is the left or right one and apply different padding on each side so it looks even. Instead, I decided to track the crease and make the cells the same width, as in the picture below:
Below is the code helper snippet that allowed me to achieve this goal. Based on it I prepared a generic view with a geometry reader that takes both parts on each side of the crease and lays them out in the way I want. The nice thing about this custom layout is that the content does not change if you half-bend the Duo — it already considers the crease.
Code:
@available(iOS 27.1, *)
extension GeometryProxy {
// The system reports the crease as inactive once the device lies flat; it is requested anyway so layouts keep splitting on it.
var creaseFrame: CGRect {
if let crease = reservedRegions(kind: .division, options: [.includeInactive]).first {
return crease.frame
}
// No crease reported: split the screen in half without a gap, along the same axis the halves use.
if size.width > size.height {
return CGRect(x: size.width / 2, y: .zero, width: .zero, height: size.height)
}
return CGRect(x: .zero, y: size.height / 2, width: size.width, height: .zero)
}
// Only while the device is bent does the system report the crease as active; lying flat, content may cross it.
var isBentAtCrease: Bool {
reservedRegions(kind: .division).contains(where: \.isActive)
}
// The part of an area that content takes on the unfolded screen: lying flat, the widest part centered on the crease
// (held tall, the whole area); bent, the half before the crease, left of it held wide and above it held tall.
func unfoldedContentFrame(in area: CGRect) -> CGRect {
let crease = creaseFrame
switch (size.width > size.height, isBentAtCrease) {
case (true, true):
return CGRect(x: area.minX, y: area.minY, width: max(.zero, crease.minX - area.minX), height: area.height)
case (true, false):
let widthOnEachSideOfCrease = max(.zero, min(crease.midX - area.minX, area.maxX - crease.midX))
return CGRect(x: crease.midX - widthOnEachSideOfCrease, y: area.minY, width: widthOnEachSideOfCrease * 2, height: area.height)
case (false, true):
return CGRect(x: area.minX, y: area.minY, width: area.width, height: max(.zero, crease.minY - area.minY))
case (false, false):
return area
}
}
}
extension CGRect {
// Held wide the crease runs from the top of the screen to the bottom. Unlike the proportions of a view, this holds
// while the keyboard is up and shortens the view.
var runsTopToBottom: Bool {
height > width
}
}
The second layout kind I developed is used in a place view. It shows a swipeable gallery of pictures on the left side and the place information on the right side. Here I also reused a crease reader and the helpers above:
In onboarding screens and registration flows I used the third type of layout where I put the graphics on one side and the information on the other:
What could break when the Duo changes shape
Now let’s go over some unexpected bugs that I found during the testing and fixed along the way:
Inside my geometry reader I had a check that would tell me which way the device is held. It only compared the view’s width and height. The bug appeared with a keyboard on my profile page when the Duo was held open tall — the keyboard shrinks the view, and the width becomes greater than the height. The form vanished into two columns of zero width because the code “thought“ the Duo is held wide
The fix was to read the direction from the crease rectangle instead of from the view itself. The crease is 40 by 951 when the device is held wide and 669 by 40 when held tall, and the keyboard does not change that.
The second problem also came from typing. The system doesn’t interrupt any user actions when they change the device shape. So you can type on the outer display, unfold the Duo in the middle of the process and continue typing without losing anything on the inner display. My user data form on the profile page with many fields would split from one column into two, and the keyboard focus was immediately lost. You need to make sure that you follow the focus — I added a Bool environment value that flips when the Duo folds or unfolds and notifies me to keep any existing focus.
Originally my screens were reading the crease frame’s live value. The bug appeared when I would do a prolonged swipe back without lifting a finger (or a mouse pointer in this case, to be precise). The content of the view I was swiping from would change on the fly, squishing everything as the view was shrinking away. I had to store the crease readings once per resting state of a screen instead of relying on the live values. Remember, the reserved region is a geometry of your view, not a fact about the device, so any time your view moves you have to decide whether you want the live value or the resting one.
The last bug came with a new default behavior of the Duo toolbar items during a scroll. When scrolling it would hide important buttons on the outer display like Save and Close, those buttons which I had deliberately kept at the top of the screen instead of moving them to the vertical bar on the right edge. UsetoolbarMinimizationBehavior(.never, for: .navigationBar)to change this.
Make sure that you test all the different combinations of Duo shapes and states of your screens thoroughly. It’s very easy to miss bugs and unexpected behaviors, even if you decide to adapt your app with minimal changes without changing the UI much.
Shipping the Duo version of your app to the App Store
You need to use Xcode 27.1 and later to submit your app update to the App Store. When Apple opened Duo submissions, they also changed the way you deal with screenshots quite a lot. Per-version screenshot sets are gone, and now all of the app’s assets get uploaded into the Asset Library, not only the screenshots and video previews, but also creative assets for page headers, search results and in-app events.
Apple didn’t stop there, and they announced that from April 2027, any app you upload to App Store Connect has to be built with the iOS 27 SDK or later. And any app built with the iOS 27.1 SDK or later has to include iPhone Duo screenshots. This is the Apple way, so we as developers are required to think about the new form factor in advance.
My open source tool appshot-studio prepares localized versions of App Store screenshots with device frames, including iPhone Duo. Check it out, if you’re interested!
Conclusion
Start by running your app in the new Duo simulator and see how it works without any changes at first. Prepare an adaptation plan for your screens, and go one by one carefully testing the flows. For those of you working for companies, start pushing your design teams now. You need to work together in this process and try to get the Duo version out before the deadline. I hope the experience I’ve shared here helps you achieve this challenging goal.