A Unity game runs on iPhone Duo without changes, but its resolution now changes while the player is playing, and most games treat resolution as fixed. The checklist below covers what that breaks, and each item links to the detail.
Unity iPhone Duo checklist
- Detect resolution changes.
Screen.widthandScreen.heightchange mid-play, so route everything screen-derived through one handler. - Re-read
Screen.safeAreaon that event, and use the rect rather than a single margin. - Choose letterbox or a wider view, per scene. Apple prefers changing the aspect ratio, with artwork in any unavoidable bars.
- Lock orientation only if you still fill the screen. Apple’s guidelines let a game lock to portrait or landscape.
- Test folding during active play. A pause on
OnApplicationPausecan leave a visibly frozen game. - Recompute input regions such as joystick bounds and tap zones whenever the resolution changes.
- Move critical HUD away from the centre of the inner display, where the fold falls.
- Build with Xcode 27.1. Only the iOS 27.1 SDK reaches the screen edge.
- Test at both display sizes, 466 × 678 and 626 × 890 points, before hardware arrives.
- Treat App Store screenshots as optional for now. Apple lists iPhone Duo sizes, but uploads open later this year.
- Build a native plugin only if the fold is part of the design.
Items 1 and 5 are the ones players will notice. Unity has published no iPhone Duo guidance, so the Unity side of this is our analysis.
Why games have it harder
Games have a harder problem than apps here. An app is built from views that expect to be resized. A game usually renders to a surface whose dimensions were read once at startup and treated as fixed for the session. On iPhone Duo, they are not.
Resolution changes while the game is running
Screen.width and Screen.height now change mid-session: when the device is folded or unfolded, when the app moves between the inner and outer displays, and when Split View is resized by the user.
Anything cached in Start() is stale from that moment. That typically includes camera viewport rects, canvas scaler reference resolutions, render textures, and any world-to-screen maths.
int _lastW, _lastH;
void Start() { _lastW = Screen.width; _lastH = Screen.height; }
void Update() {
if (Screen.width != _lastW || Screen.height != _lastH) {
_lastW = Screen.width;
_lastH = Screen.height;
OnResolutionChanged();
}
}
void OnResolutionChanged() {
// Rebuild render targets, recompute viewport rects, refresh UI anchors
}
Polling is unglamorous but reliable, and the check is cheap. The alternative — assuming a resolution change implies a scene reload — does not hold here, because the player can fold the device mid-level.
Render textures are the expensive case. Reallocating every frame during a fold animation will hitch, so debounce: wait for dimensions to settle for a few frames before reallocating.
Safe area
Screen.safeArea is the right API and it works correctly. The trap is the same one native apps hit:
// Wrong — asserts the two sides match
float margin = Screen.safeArea.x;
rect.offsetMin = new Vector2(margin, rect.offsetMin.y);
rect.offsetMax = new Vector2(-margin, rect.offsetMax.y);
// Right — use the rect as given
Rect safe = Screen.safeArea;
rect.anchorMin = new Vector2(safe.x / Screen.width, safe.y / Screen.height);
rect.anchorMax = new Vector2((safe.x + safe.width) / Screen.width,
(safe.y + safe.height) / Screen.height);
Apple states safe areas are asymmetric on iPhone Duo, so any single-margin derivation is wrong on at least one edge.
Note that Screen.safeArea must be re-read after every resolution change, alongside everything else.
Aspect ratio is the design decision
The inner display is roughly 1.42. Most mobile games are tuned for something near 2.17. That gap is large enough to be a design decision rather than a scaling parameter.
Two honest options, and they should be chosen per scene:
Letterbox to a fixed aspect. Predictable, safe, and preserves your composition exactly. On a 1.42 display it leaves substantial bars — around a fifth of the screen for 16:9 content. For a game with hand-authored levels tuned to a specific frame, this is often correct.
Extend the camera frustum. Fills the display, but shows more of the world than the level was designed to reveal. For a 2D platformer this can expose off-screen enemies or unbuilt geometry. For a 3D game with a full environment it is usually free.
The version to avoid is scaling to fill with a fixed aspect, which crops around a quarter of the frame width. In a game where the edges carry information — a HUD, an enemy indicator, a minimap — that is a functional regression, not a cosmetic one.
The hinge
Division regions have no Unity binding. Reaching them means a native plugin wrapping reservedRegions(kind:) and marshalling frames into C#.
That is real work, and for most games there is a cheaper mitigation that captures most of the benefit: keep critical HUD elements away from the centre of the inner display, where the fold falls. You cannot know exactly where the fold is without the native API, but you know roughly where it is, and moving a health bar away from centre is a five-minute change.
For a game where the fold is genuinely part of the experience — a board game across the crease, a two-player mode with the device propped — the plugin is worth building. For a runner or a puzzle game, it is not.
Lifecycle: the freeze bug
This is the failure most likely to reach players.
Folding the device can move your app between displays without backgrounding it, and in Split View your game can be visible but not frontmost. A common Unity pattern is:
void OnApplicationPause(bool paused) {
Time.timeScale = paused ? 0f : 1f;
}
On iPhone Duo that can leave a visibly frozen game on screen. Test folding and unfolding during active play, not from a paused menu — and consider distinguishing “not frontmost but visible” from “backgrounded” before stopping simulation.
Input
Touch coordinates are reported in the current screen space, so they follow resolution changes automatically. What does not follow is any input region computed from cached dimensions — virtual joystick bounds, tap zones, swipe thresholds expressed in pixels. Recompute these in OnResolutionChanged along with everything else.