← 모든 글

iPhone Duo에 맞춰 앱 준비하기: 실제 앱으로 따라가는 예제

iPhone Duo에 맞춰 앱을 어떻게 준비해야 합니까? iOS 27.1 SDK로 빌드하고, 시뮬레이터에서 모든 포즈를 차례로 거쳐 보고, 그 과정에서 드러난 문제를 고치고, Apple이 아직 발표하지 않은 두 날짜에 맞춰 제출을 준비하면 됩니다. iPhone Duo는 iOS 27.1을 탑재하고 10월 23일에 출시됩니다.1 그 위에서 앱이 무엇을 받는지는 바이너리에 찍힌 SDK 버전이 결정합니다. 같은 소스 파일을 26.0, 27.0, 27.1로 각각 링크했더니, 휴대폰 크기의 상자, 상태 레일 옆의 창, 그리고 화면 전체(바는 측면으로 내려가고 접힘도 보고됨)로 각각 떴습니다.12 10월 2일 현재 27.1 이상을 찍을 수 있는 것은 Apple의 베타뿐이고(Duo 시뮬레이터가 있는 Xcode 27.1 beta와 Xcode 27.2 beta), App Store Connect는 그 빌드를 스토어가 아니라 TestFlight용으로만 받습니다.8 이 글은 저희가 TestFlight에서 배포 중인 카드 수집가용 앱 Kiradex로 이 작업 전체를 처음부터 끝까지 해 봅니다. 공짜로 얻은 것, 코드를 읽어서는 몰랐는데 직접 돌려 보며 잡아낸 것, 두 디스플레이용 스크린샷, 그리고 코딩 에이전트에게 넘길 수 있는 브리프까지 다룹니다.

TL;DR

  • SDK 스탬프가 스위치입니다. iOS는 바이너리의 LC_BUILD_VERSION에 있는 SDK 버전을 읽습니다. 27.1로 찍힌 프로브는 화면 전체를 받았습니다. 접은 상태에서 466 × 678 포인트, 펼친 상태에서 951 × 669 포인트였고, 세로 바와 예약 영역도 함께였습니다. 27.0으로 찍힌 프로브는 상태 레일이 있는 가장자리에서 80포인트 못 미쳐 멈췄고, 바는 가로로 남았으며, 접힘에 대해서는 아무것도 전달받지 못했습니다. 26.0으로 찍힌 프로브는 상자 안에 든 375 × 667 휴대폰이었습니다. 자신의 빌드는 otool -l로 확인하세요.12
  • 달력에는 미정인 날짜가 두 개 있습니다. TestFlight는 9월 18일부터 27.1 SDK 빌드를, 9월 16일부터 27.2 beta 빌드를 받고 있습니다. App Store는 27.0 SDK 빌드만 받습니다. Duo 스크린샷 크기는 공개되었지만 업로드는 “will be available later this year”(올해 후반에 제공될 예정)라고 되어 있습니다.89 업로드할 때 런치 스크린 키가 이제 필수입니다.10
  • 대부분은 시스템 컨테이너가 해 주었습니다. TabView, 다섯 탭 중 네 탭에 들어 있는 NavigationStack, 제목과 심볼을 함께 가진 툴바 항목. 이것만으로 Duo 전용 코드 없이 Kiradex의 바가 측면으로 옮겨 갔습니다. ArrangementView 하나가 펼친 디스플레이를 나누었고, Book 포즈에서는 구분선을 접힘 위로 옮겼습니다. onHingeChange 하나가 휴대폰을 펼칠 때 앱의 오프닝을 다시 재생합니다.13
  • 직접 돌려 보니 코드를 읽어서는 몰랐던 것이 잡혔습니다. 버튼이 텍스트 “Done” 하나뿐인 시트는 측면 레일을 확보해 놓고 비워 둡니다. 전체 화면 카드는 외부 카메라 아래로 들어갑니다. 세로로 돌리면 분할이 위아래로 쌓입니다. 접힘을 건너 열어 둔 카드는 compact 레이아웃이 늘어난 모습으로 나타납니다. 내부 디스플레이용으로 짠 regular 폭 레이아웃은 가로로 놓인 6.9인치 iPhone에서 깨집니다.13
  • 스토어가 27.1을 받기 전까지는 소스 트리 하나에 Xcode 두 개가 필요합니다. #available로는 해결되지 않습니다. 27.0 SDK에는 컴파일할 ArrangementView가 없기 때문입니다. 컴파일 조건 플래그나 #if canImport(SwiftUI, _version: 8.0.85)로 새 호출을 감쌉니다.14
  • 스크린샷도 같은 실행에서 나옵니다. 시뮬레이터 캡처는 App Store Connect의 크기와 정확히 같고, Apple의 Duo 베젤 개구부도 마찬가지입니다. 그래도 Apple의 규칙은 정면에서, 수정 없이, 기기의 3D 렌더링은 금지라고 말합니다.911

10월 2일 현재 상황

상태 날짜
기기 사전 주문 10월 16일, 출시 10월 23일, “available with iOS 27.1”(iOS 27.1과 함께 출시)1 9월 9일
SDK Xcode 27.1 beta(27A9269)가 iOS 27.1 SDK와 iPhone Duo 시뮬레이터를 탑재. Apple의 Releases 피드에는 두 번째 27.1 베타도 릴리스 후보도 없음. Xcode 27.2 beta는 같은 API를 갖추었지만 Duo 시뮬레이터는 없음814 9월 18일, 피드는 10월 2일 확인
TestFlight Xcode 27.1 beta와 Xcode 27.2 beta의 빌드를 내부·외부 테스트 모두 수용8 9월 16일, 18일, 28일
App Store Xcode 27과 27.0 SDK의 빌드를 수용. 27.1 SDK에 문을 여는 항목은 없음8 9월 14일
Duo 스크린샷 크기 공개, 업로드는 “will be available later this year”(올해 후반 제공 예정)9 9월 9일
런치 스크린 iOS 27 SDK 이상으로 빌드한 모든 앱에 업로드 시 필수10 기술 노트 9월 14일 개정

이 중 세 행은 Apple을 기다리는 중이지만, 어느 것도 작업을 막지는 않습니다. 표에서 나오는 순서는 이렇습니다. 앱을 27.1 SDK로 올려 시뮬레이터에 넣고, 모든 포즈를 거쳐 보고, 드러난 문제를 고치고, 그 빌드를 TestFlight에 올립니다. 그리고 스토어 제출과 Duo 스크린샷은 각각 문이 열리는 날을 위해 준비해 둡니다.

Apple이 제시하는 출발점은 여섯 편의 Tech Talk 중 첫 번째, 10분짜리 영상이며, 아래 내용은 모두 이 영상을 봤다고 가정합니다.

Watch on Apple Developer ↗

1단계: SDK 스탬프가 기기에서 받을 것을 결정합니다

토크는 세 단계로 된 호환성 약속으로 시작합니다. iOS 27 SDK로 빌드하지 않은 앱도 실행됩니다. “When the device is closed, your app will use the screen space to the left of the status bar and camera. When the device is open, your app will be a familiar size and aspect ratio.”(기기를 접으면 앱은 상태 표시줄과 카메라 왼쪽 공간을 쓰고, 펼치면 익숙한 크기와 화면비가 된다)(0:36) iOS 27 SDK를 채택한 앱은 “will extend to the left of the status bar area on the inner display.”(내부 디스플레이에서 상태 표시줄 영역 왼쪽까지 확장된다)(0:58) 그리고 이어서 이렇게 말합니다. “When you build your app with the iOS 27.1 SDK, your app extends to the edge of the screen. Standard navigation and toolbar buttons now lay out vertically under the status bar.”(iOS 27.1 SDK로 빌드하면 앱이 화면 가장자리까지 확장되고, 표준 내비게이션 버튼과 툴바 버튼은 상태 표시줄 아래에 세로로 배치된다)(1:05)3

이 세 단계는 코드의 속성이 아닙니다. 바이너리 안의 숫자 하나, 즉 링커가 LC_BUILD_VERSION 로드 명령에 기록하는 SDK 버전의 속성이며, iOS는 실행 시점에 이 값을 읽습니다. 이 숫자에 얼마나 많은 것이 걸려 있는지 보려고 작은 프로브를 하나 만들었습니다. 다섯 항목짜리 툴바를 가진 NavigationStack과 ArrangementView를 담은 TabView입니다. 이것을 같은 소스 파일에서 세 번 빌드했습니다. 세 바이너리의 차이는 그 숫자, 즉 26.0, 27.0, 27.1뿐입니다. 그리고 세 개 모두를 iPhone Duo 시뮬레이터에서 모든 포즈로 실행했습니다.12

iPhone Duo 시뮬레이터의 펼친 내부 디스플레이에 띄운 같은 프로브 앱 세 개. SDK 26.0으로 링크한 것은 검은 화면 위의 휴대폰 크기 창으로, 375 × 667 포인트이며 툴바와 탭 바가 가로로 놓임. SDK 27.0으로 링크한 것은 검은 상태 레일 왼쪽의 화면을 채우며 871 × 669 포인트, 바는 가로. SDK 27.1로 링크한 것은 화면 전체를 채우며 951 × 669 포인트, 툴바와 탭 바가 오른쪽 가장자리의 시계 아래에 세로로 쌓이고, division 하나와 occlusion 두 개를 보고함

소스 파일 하나, SDK 스탬프 셋, 펼쳐서 똑바로 세운 상태. 왼쪽부터 26.0, 27.0, 27.1.

포즈 26.0 스탬프 27.0 스탬프 27.1 스탬프
Closed 375 × 667, 레일 옆 공간에 축소되어 들어감 386 × 678, 레일 옆 466 × 678, 디스플레이 전체
Open 375 × 667, 화면 한가운데의 휴대폰 871 × 669 951 × 669
Open, 회전 375 × 667 669 × 871 669 × 951
Closed, 회전 667 × 375 678 × 386 678 × 466
폭 size class, Open compact regular regular
바 어디서나 가로 어디서나 가로 세로, 단 펼쳐서 회전한 경우 제외
toolbarVerticalEdge nil nil trailing, 단 펼쳐서 회전한 경우에는 nil
예약 영역 없음 없음 접힘, 두 카메라, 상태 열
반쯤 접은 상태 변화 없음 변화 없음 접힘이 활성 division이 되고, split arrangement는 그 위에 틈을 엶

창 크기는 포인트 단위이며, 각 빌드의 창이 스스로 보고한 값입니다.12

가운데 열은 두 번 읽으십시오. 대부분의 앱이 곧 출시하게 될 것이 바로 이 열이기 때문입니다. Xcode 27로 빌드한 앱은 내부 디스플레이에서 regular 폭과 화면의 대부분을 받습니다. 첫 번째 열의 ‘상자 속 휴대폰’에 비하면 실질적인 개선입니다. 하지만 상태 레일이 있는 trailing 가장자리에서 80포인트 못 미쳐 멈추고, 세로 바를 받지 못하며, 시스템은 접힘이나 카메라에 대해 아무것도 알려 주지 않습니다. 예약 영역 질의는 모든 포즈에서, 프로브가 몇 번을 물어도 전부 비어 있었습니다.12 스탬프가 공정한 대용품인지 확인하려고, 프로브의 단순한 버전을 Xcode 27.0 자체로 그 자신의 iOS 27.0 SDK에 맞춰 컴파일해 보기도 했습니다. 창 크기는 같았습니다. 접은 상태 386 × 678, 펼친 상태 871 × 669였습니다.12

이 부분에서 Apple의 문서 가이드는 토크보다 덜 정확하고, 문구도 바뀌었습니다. 9월 17일 개요에는 이렇게 적혀 있었습니다. “Build your app with Xcode 27.1 or later to use all of the available screen space on iPhone Duo. In earlier versions, your app doesn’t extend under the status bar and camera.”(iPhone Duo의 화면 공간을 모두 쓰려면 Xcode 27.1 이상으로 빌드하라. 이전 버전에서는 앱이 상태 표시줄과 카메라 아래로 확장되지 않는다) 9월 19일에는, 10월 2일 지금과 마찬가지로 이렇게 바뀌어 있었습니다. “Build your app with the latest version of Xcode to use all of the available screen space on iPhone Duo. When you build with Xcode 26 and earlier, your app doesn’t extend under the status bar and camera.”(최신 버전의 Xcode로 빌드하라. Xcode 26 이하로 빌드하면 상태 표시줄과 카메라 아래로 확장되지 않는다)2 새 문장이 부족하다고 지목하는 것은 Xcode 26 이하뿐이고, “latest version”이 베타를 포함하는지는 말하지 않습니다. 그래서 독자는 Xcode 27.0 빌드가 화면 전체를 받는다고 읽을 수 있습니다. 이 베타의 시뮬레이터에서는 그렇지 않습니다. 받는 것은 토크가 말한 가운데 단계이고, 예전 문구가 제 측정과 일치했습니다. 실제 하드웨어가 다른 결과를 보여 주기 전까지는 표대로 계획하십시오.

그러니 무엇보다 먼저, 테스트 중인 빌드의 스탬프를 확인하십시오.

otool -l Build/Products/Debug-iphonesimulator/YourApp.app/YourApp | grep -A4 LC_BUILD_VERSION

Xcode의 디버그 빌드는 코드를 작은 런처 옆의 YourApp.debug.dylib에 두며, 둘 다 이 명령을 가지고 있습니다. 찾아야 할 것은 sdk 27.1 이상입니다. Xcode 27.2 beta의 SDK가 기록할 27.2를 찍은 프로브도 27.1과 같은 대우를 받았습니다.12 Kiradex의 값은 minos 27.0과 sdk 27.1입니다. iOS 27.0에 설치되고, Duo에서는 27.1의 동작을 받습니다.13

이 점을 강조하는 이유는 제가 공개적으로 틀렸기 때문입니다. 9월 21일에 쓴 이 시뮬레이터에 대한 첫 보고에서 저는 툴바가 한 번도 세로가 되지 않았고 예약 영역은 모든 포즈에서 비어 있었다고 썼습니다. 베타에는 문제가 없었습니다. 저는 그 프로브를 swiftc -sdk를 직접 호출해 컴파일했고, 링크 단계가 같은 Xcode 안의 macOS SDK를 루트로 잡으면서 바이너리가 27.0으로 찍혀 나갔습니다. 그날 측정한 모든 것이 가운데 열이었던 셈입니다. 그 글에는 지금 정정이 실려 있습니다.12 시뮬레이터에서 Duo 레이아웃이 반쯤만 적용된 것처럼 보인다면, 즉 바가 위쪽을 가로지르고 측면에 죽은 검은 띠가 내려간다면, 코드를 보기 전에 스탬프부터 확인하십시오.

작업대 위의 앱

Kiradex는 저희가 TestFlight에서 배포 중인 트레이딩 카드 수집가용 앱입니다. 모든 세트, 가진 카드와 갖고 싶은 카드, 시간에 따른 가치, 그리고 뒤집어 볼 수 있는 3D 오브젝트로서의 각 카드를 다룹니다. 무료이고, iPhone 전용이며, iOS 27.0 이상에서 실행되고, 수집가의 목록은 본인의 iCloud에 저장되어 중간에 저희 서버가 끼지 않습니다.13 완성된 앱은 아닙니다. 스캐너는 실제 카메라를 한 번도 만나 보지 못했고, 이번 주까지 펼친 Duo에서 이 앱을 본 사람은 없었습니다. 내부 디스플레이 레이아웃 옆에 로드맵은 “Seen on the open Duo’s inner display only by the column maths; the simulator was folded shut.”(펼친 Duo의 내부 디스플레이는 열 계산으로만 봤다. 시뮬레이터는 접힌 채였다)라는 줄을 달고 있었습니다.13 그래서 공정한 시험이 됩니다. 이 앱의 Duo 작업은 Apple 문서를 보고 작성되었고 한 번도 화면에서 확인되지 않았습니다.

이 앱이 Duo를 위해 하는 일은 적습니다. 그중 Duo 전용 코드가 얼마나 적은지 보여 주기 위해 나열해 볼 만합니다.

  • 내비게이션은 시스템의 것입니다. 다섯 탭을 가진 TabView이고, 그중 하나는 검색 역할이며, 네 탭에 NavigationStack이 들어 있습니다. 스캐너는 자체 바가 없는 카메라 뷰입니다. 툴바 항목은 제목과 심볼을 가진 Label이고 .toolbar로 붙입니다. 직접 만든 바는 어디에도 없습니다.
  • 레이아웃은 size class를 따릅니다. compact 폭에서는 세 열짜리 바인더 페이지가 되고 카드의 인스펙터는 스택에 푸시됩니다. regular 폭은 iPhone 전용 앱에서는 대개 Duo의 내부 디스플레이를 뜻하는데, 짝수 열의 카드 벽이 되며, 카드 하나를 고르면 화면이 페이지와 인스펙터로 나뉩니다.
  • 27.1 SDK 호출은 두 개입니다. 분할은 .split 스타일의 ArrangementView이고, iOS 27.1 미만에서는 HStack으로 대체됩니다. 그리고 onHingeChange가 휴대폰이 접힌 상태에서 다른 상태로 바뀔 때 앱의 오프닝 애니메이션, 즉 빨간 기기가 열리는 장면을 다시 재생합니다.
  • 방향 잠금은 없고, 런치 스크린은 있습니다. Info.plist는 세로와 양쪽 가로 방향을 나열하고 UILaunchScreen을 선언합니다.13

이것이 전부입니다. 두 파일에 availability 검사 두 개. 그 밖에 기기가 이 앱에 하는 일은, 이런 방식으로 빌드한 어떤 앱에도 똑같이 합니다.

이 글의 이미지는 앱이 읽어 오는 공개 카탈로그의 카드 이미지와 함께, 실제로 실행 중인 앱을 보여 줍니다. Kiradex는 독립적인 수집가용 도구이며 Nintendo, Creatures Inc., GAME FREAK inc., The Pokémon Company와 제휴하거나 그들의 승인을 받지 않았고, 카드 이미지는 각 권리자의 소유입니다.

2단계: 모든 포즈를 거쳐 보기

Apple의 가이드는 이 과정을 네 가지 점검으로 제시합니다.2

  • “Confirm your views resize well in each supported orientation and pose.”(지원하는 각 방향과 포즈에서 뷰가 제대로 크기 조정되는지 확인하라)
  • “Inspect how the system presents your app’s navigation bars, toolbars, and tab bars vertically on the side of the display.”(시스템이 앱의 내비게이션 바, 툴바, 탭 바를 디스플레이 측면에 세로로 표시하는 방식을 살펴보라)
  • “Identify any views, sheets, or popovers that position awkwardly when you fold or open iPhone Duo.”(iPhone Duo를 접거나 펼 때 어색하게 놓이는 뷰, 시트, 팝오버를 찾아내라)
  • “Identify elements or controls in your views that appear in the fold, and are difficult to see or interact with.”(접힘에 걸려 보기 어렵거나 조작하기 어려운 요소나 컨트롤을 찾아내라)

디자인 가이드는 레이아웃이 몇 개 필요한지 이렇게 말합니다. “A compact width layout for the outer display and a regular width layout for the inner display give you the fundamentals for every pose.”(외부 디스플레이용 compact 폭 레이아웃과 내부 디스플레이용 regular 폭 레이아웃이 있으면 모든 포즈의 기본이 갖춰진다)7

포즈는 Device Hub의 버튼으로, Closed, Book, Open, Rotate Right가 있으며, 조합마다 다른 화면이 됩니다. SwiftLee의 가이드는 제게는 필요 없었던 더 세밀한 조작을 덧붙입니다. “Holding Option ⌥ reveals a slider that you can use to control the hinge angle of the device with precision”(Option ⌥을 누르고 있으면 기기의 힌지 각도를 정밀하게 조절하는 슬라이더가 나타난다)17 앱을 보기 전에, 각 포즈에서 시스템이 어떤 앱에든 무엇을 건네는지 알아 두면 도움이 됩니다. 다음은 27.1 스탬프 프로브가 읽은 값입니다.12

포즈 창(포인트) size class(폭, 높이) 바 보고된 예약 영역
Closed 466 × 678 compact, regular 세로, trailing 가장자리 외부 카메라 37 × 37 활성, 상태 열 84 × 170 활성
Closed, 회전 678 × 466 compact, compact 세로, trailing 가장자리 외부 카메라 활성, 상태 영역 84 × 82 활성
Open 951 × 669 regular, regular 세로, trailing 가장자리 접힘 폭 40, x 455에서 495, 비활성, 내부 카메라 58 × 37 비활성, 상태 열 84 × 120 활성
Book 951 × 669 regular, regular 세로, trailing 가장자리 같은 세 개, 접힘은 활성
Open, 회전 669 × 951 regular, regular 가로 접힘 높이 40, y 455에서 495, 비활성, 내부 카메라 비활성, 상태 영역 134 × 82 활성
Book, 회전 669 × 951 regular, regular 가로 같은 세 개, 접힘은 활성

27.1 SDK로 빌드한 앱에 시스템이 보고하는 영역을 iPhone Duo 시뮬레이터의 세 화면에 비율대로 그린 그림. Closed는 466 × 678 포인트로, 폭 382포인트의 콘텐츠 영역과 오른쪽에 상태 열, 카메라, 바를 담은 폭 84포인트의 레일. Open은 951 × 669 포인트로, 폭 867포인트의 콘텐츠 영역, 같은 레일, 가운데를 세로로 지나는 접힘용 40포인트 띠, 그 오른쪽 위쪽에 내부 카메라. 펼쳐서 회전한 상태는 669 × 951 포인트로, 위아래에 가로 바, 가운데를 가로지르는 40포인트 띠로서의 접힘

이 표의 세 가지가 그 뒤의 모든 것을 결정했습니다.

휴대폰을 접어도 창은 바뀌지 않습니다. Book과 Open은 같은 크기를 보고합니다. 앱이 볼 수 있는 차이는 접힘의 division이 비활성에서 활성으로 바뀌는 것, 그리고 힌지가 180도 완전히 펼침에서 127도 부분 펼침으로 바뀌어 보고되는 것뿐입니다. 크기만 읽는 앱은 휴대폰이 접혔다는 사실을 끝내 알지 못합니다.

접힘은 항상 거기 있고, 작습니다. 펼쳤을 때든 접었을 때든 정확히 가운데에 보고되며, API가 돌려주는 프레임은 두 상태 모두 폭 40포인트이고 양쪽에 20포인트씩 여백이 있습니다. Apple의 토크는 접힘 영역에 대해 “When flat, it’s inactive and has a width of zero.”(평평할 때는 비활성이며 폭은 0이다)(7:32)라고 말합니다. 시뮬레이터는 폭 0인 프레임을 돌려주지 않습니다. Artem Novichkov의 현장 노트는 같은 값을 “40 pt wide, with 20 pt margins on each side of a zero-width fold line,”(폭 40pt, 폭 0인 접힘 선 양쪽에 20pt 여백)이라고 설명하며,17 저도 토크의 0을 여백 사이의 선으로 같은 방식으로 읽습니다. 이것은 제 해석이며 Apple의 글에 적힌 내용이 아닙니다. 토크는 실용적인 요점도 짚습니다. “By default, only active ones will be returned, but you can query for inactive ones”(기본적으로 활성 영역만 반환되지만 비활성 영역도 질의할 수 있다), 그리고 “in grid-like layouts, you could prefer even numbers of columns when a division region is present regardless of its active state.”(격자형 레이아웃에서는 division 영역이 있으면 활성 여부와 상관없이 짝수 열을 택할 수 있다)(7:16, 7:41)5

영역은 늦게 도착합니다. 매번 실행할 때마다 프로브의 처음 몇 번의 평가에서는 영역이 전혀 읽히지 않았고, 그 뒤의 모든 평가에서는 전체가 읽혔습니다. 첫 레이아웃 패스에서 한 번만 묻는 뷰는 빈 배열을 캐시하게 됩니다. 뷰의 body에서, 평가될 때마다 읽으십시오. 프로브는 1초 타이머로 다시 평가했고, Artem Novichkov의 노트는 그 규칙을 직접 적어 둡니다. “Read them in the GeometryReader body so the view updates when they arrive. Don’t cache them.”(GeometryReader의 body에서 읽어야 도착했을 때 뷰가 갱신된다. 캐시하지 마라)1217

앱으로 넘어가기 전에 실용적인 메모 하나. 이 베타의 iOS 27.1 시뮬레이터 런타임은 정확히 한 가지 기기 유형, iPhone Duo만 지원합니다. iPhone 18 Pro Max를 요청하면 “Incompatible device”로 실패합니다. 그래서 아래의 비교 중 같은 빌드를 일반 iPhone에서 돌린 것은 iOS 27.0 런타임에서 실행했고, 이는 #available 대체 경로가 작동하는지 빠르게 확인하는 방법이기도 합니다.12

한 빌드의 Kiradex가 같은 카드 세트를 세 화면에 보여 줌. 6.9인치 iPhone에서는 카드 세 열, 위쪽을 가로지르는 내비게이션 바와 아래쪽을 가로지르는 탭 바. 접은 iPhone Duo에서는 세 열이고, 뒤로 버튼, 필터 버튼, 탭 버튼 다섯 개가 시계 아래 오른쪽 가장자리의 레일에 세로로 쌓임. 펼친 iPhone Duo에서는 카드 여섯 열과 오른쪽의 같은 레일

6.9인치 iPhone, 접은 Duo, 펼친 Duo에서 실행한 Kiradex 빌드 하나. 앱 코드에는 바를 측면에 두는 부분이 전혀 없습니다.

직접 거쳐 보며 찾은 것

UI 테스트가 빌드 5를 여섯 포즈 각각에서 여덟 종류의 화면으로 데려갔고(접고 회전한 상태에서는 Settings 버튼이 오버플로 메뉴로 옮겨 가 있어서 일곱 종류), 스크립트가 각 정지 지점에서 두 디스플레이를 모두 캡처했습니다. 외부는 1398 × 2034 픽셀, 내부는 2853 × 2007 픽셀로, App Store Connect가 제시하는 크기입니다.13 Apple의 네 가지 점검에 비추어 보면, 이 앱은 예상보다 많은 항목을 통과했고, 코드를 아무리 읽어도 걸리지 않았던 곳에서 실패했습니다.

공짜로 얻은 것

바. 접은 상태에서는 툴바와 다섯 탭이 모두 시계 아래 레일에 섭니다. 펼쳐도 같습니다. 펼쳐서 회전하면 위와 아래로 돌아갑니다. 앱은 그 어느 것도 요청하지 않습니다. 두 번째 토크는 직접 만든 바가 뒤처지는 이유를 설명합니다. “When used to build custom bars, content from sub-components like UIToolbar, UINavigationBar, or UITabBar won’t be considered.”(커스텀 바를 만드는 데 쓰면 UIToolbar, UINavigationBar, UITabBar 같은 하위 컴포넌트의 콘텐츠는 고려되지 않는다)(2:52)4 그리고 주요 화면의 툴바 항목은 모두 Label이므로, 각각 레일용 심볼과 오버플로 메뉴용 제목을 갖고 있습니다. 이것이 가이드의 또 다른 조건입니다. “If your item has a title and doesn’t have an icon, the system doesn’t present it vertically.”(항목에 제목은 있고 아이콘이 없으면 시스템은 그것을 세로로 표시하지 않는다)2

Watch on Apple Developer ↗

접힘. 펼친 상태에서 세트의 카드는 가로로 여섯 장, 짝수로 걸리므로 화면 한가운데에 카드가 놓이지 않습니다. 하나를 고르면 화면은 앱 콘텐츠 영역의 가운데인 x 433에서 페이지와 카드 인스펙터로 나뉩니다. 접힘보다 42포인트 왼쪽인데, 레일이 오른쪽에서 84포인트를 가져가기 때문이며, 휴대폰이 평평한 동안에는 문제가 되지 않습니다. Book 포즈에서는 arrangement 뷰가 구분선을 접힘 위로 옮기고 40포인트 띠를 비워 둡니다. x 433에서 시작하던 인스펙터가 이제 495에서 시작합니다. 앱에는 Book 포즈를 위한 코드가 전혀 없습니다. 이것은 토크가 설명하듯 ArrangementView가 “moving, resizing, or reorganizing what’s already there.”(이미 있는 것을 옮기고, 크기를 바꾸고, 재구성하는 것)(6:04)를 하고 있는 것입니다.513

카드를 선택한 상태에서 펼친 내부 디스플레이의 Kiradex 두 장. 평평한 상태에서는 왼쪽에 세 열 카드 페이지, 오른쪽에 선택한 카드의 3D 스테이지와 세부 정보가 있고, 콘텐츠 영역의 가운데, 즉 화면 가운데보다 약간 왼쪽에서 만남. Book 포즈에서는 같은 두 창 사이 접힘 위치에 빈 세로 띠가 있고, 페이지는 약간 넓고 인스펙터는 좁음

펼쳐서 평평한 상태, 이어서 Book 포즈. 두 번째 그림의 틈이 접힘이며, 앱이 넣은 것이 아닙니다.

펼쳤을 때의 시트. 내부 디스플레이에서 시트는 가운데에 나타나고 Done 버튼은 가로로 남습니다. Book 포즈에서 프로브의 시트는 스스로 접힘의 leading 쪽으로 이동했습니다.12

이벤트로서의 힌지. 앱을 실행한 채 휴대폰을 펼치면 내부 디스플레이에서 오프닝이 다시 재생됩니다. 접힌 빨간 기기가 열리면서 앱으로 이어집니다. 상태가 closed를 벗어날 때 발생하는 onHingeChange 핸들러 하나이며, 네 번째 토크가 요구하는 역할 분담 그대로입니다. “Hinge data is observed live, and is ideal for driving interactions or effects. For layout, use the arrangement and region APIs.”(힌지 데이터는 실시간으로 관찰되며 상호작용이나 효과를 구동하는 데 이상적이다. 레이아웃에는 arrangement와 영역 API를 써라)(2:34)6

Kiradex를 실행한 채 시뮬레이터를 펼칠 때 iPhone Duo 내부 디스플레이에서 캡처한 네 프레임. 짙은 빨강 바탕 위의 접힌 빨간 휴대용 기기, KIRADEX라고 적힌 작은 초록 화면을 보이며 열리는 기기, 앱 위로 서서히 사라지는 열린 기기, 그리고 앱의 Dex 화면

앱을 실행한 채 펼친 모습. 오프닝은 앱이 직접 그린 앱 자신의 기기이고, 힌지가 그것을 다시 재생합니다.

놓친 것

텍스트 버튼 하나뿐인 시트는 레일을 확보해 놓고 비워 둡니다. 접은 상태에서 Settings 시트는 trailing 가장자리를 따라 세로 바를 위한 띠를 내주고는 거기에 아무것도 두지 않습니다. 유일한 툴바 항목이 제목만 있고 심볼이 없는 “Done”인데, 텍스트만 있는 항목은 가로로 남기 때문입니다. 폼은 남은 공간에 눌려 들어갑니다. 프로브가 그 손실을 측정했습니다. 레일을 확보하면 콘텐츠 폭 374포인트, 세로 바를 끄면 450포인트입니다.12 Apple의 토크는 바로 이 경우를 언급합니다. “if a sheet is control-heavy with only one item, like the close button here, consider disabling a vertical bar so it doesn’t reduce available space.”(시트가 컨트롤 위주인데 여기 닫기 버튼처럼 항목이 하나뿐이라면, 사용 가능한 공간을 줄이지 않도록 세로 바를 비활성화하는 것을 고려하라)(14:37)4 고치는 방법은 두 가지이고, 프로브로 둘 다 실행해 봤습니다. 버튼에 심볼을 주는 것, Button("Done", systemImage: "checkmark"). 그러면 버튼이 눈에 띄는 체크 표시로 레일 안에 들어갑니다. 또는 시트 콘텐츠에 .toolbarVerticalBehavior(.disabled)를 붙이는 것. 그러면 레일이 사라지지만 대신 시트가 짧아집니다.

접은 외부 디스플레이의 캡처 네 장. Kiradex의 Settings 시트는 위쪽에 텍스트 Done 버튼이 있고 오른쪽에 시계만 담긴 빈 띠가 있음. 텍스트 Done을 가진 프로브의 시트에도 같은 빈 띠. Done에 체크 표시 심볼을 단 프로브의 시트에서는 버튼이 시계 아래 그 띠에 파란 원으로 자리함. 세로 바를 비활성화한 프로브의 시트는 전체 폭에 더 짧고, Done은 오른쪽 위에 있음

왼쪽부터 앱의 시트, 이어서 텍스트만 있는 Done, 심볼을 단 Done, 세로 바를 비활성화한 프로브.

전체 화면 카드가 카메라 아래로 들어갑니다. 이 앱의 대표 화면은 바를 숨긴 검은 스테이지 위의 카드이고, 그 스테이지는 모든 가장자리에서 세이프 에어리어를 무시합니다. 외부 디스플레이에서는 그 때문에 카드의 위쪽 모서리가 카메라 아래로 들어갑니다. 시스템은 묻는 모든 뷰에 그 카메라를 37포인트 정사각형의 활성 occlusion으로 보고하며, 그 위치는 Apple의 베젤 이미지에 있는 컷아웃과 1.5포인트 이내로 일치합니다.1112 Apple의 디자인 가이드는 전체 폭 처리를 “as long as nothing conflicts with the Dynamic Island or the status bar.”(Dynamic Island나 상태 표시줄과 충돌하지 않는 한) 허용합니다.7 고치는 방법은 검은 바탕은 가장자리까지 채우되 카드 자체는 안쪽으로 들이는 것입니다. 이 디스플레이에서는 스테이지가 trailing과 위쪽 세이프 에어리어를 지키게 하거나, reservedRegions(kind: .occlusion)을 읽어 돌아온 영역을 피해 카드를 배치하십시오.

접은 외부 디스플레이에서 캡처한 Kiradex의 전체 화면 카드 뷰어를 Apple의 iPhone Duo 베젤 안에 넣은 모습. 카드가 화면을 채우고, 그 오른쪽 위 모서리가 디스플레이 구석의 카메라 아래, 뷰어의 닫기 버튼 옆을 지나감

외부 디스플레이용 Apple 베젤 안에 넣은 뷰어 캡처. 시뮬레이터 스크린샷에는 구멍이 없지만, 휴대폰에는 있습니다.

회전하면 두 창이 위아래로 쌓입니다. 펼쳐서 세로로 돌리면 디스플레이 폭은 669포인트이고 여전히 regular 폭이므로 앱은 계속 분할합니다. 그런데 폭보다 높이가 큰 공간을 채우는 arrangement 뷰는 위아래로 분할합니다. Apple의 가이드에 따르면 “it places the primary view on top and the secondary view below it when the containing view is taller than it is wide.”(감싸는 뷰가 폭보다 높이가 크면 주 뷰를 위에, 보조 뷰를 그 아래에 둔다)입니다.2 그 결과 인스펙터 위에 카드 한 줄과 두 번째 줄의 윗부분만 보이는 띠가 생깁니다. 그 줄에 달린 앱 자체의 주석은 이 짝이 “is only useful side by side,”(나란히 놓일 때만 쓸모 있다)라고 말하지만, 그것을 강제하는 장치는 없었습니다. .split.axes(.horizontal)로 스타일을 제한하는 것이 문서에 나온 제어 방법이며,16 토크가 짚어 주는 함정이 있습니다. “If the split arrangement cannot split among an axis, and it’s the primary axis, the arrangement view chooses to only show a single view.”(split arrangement가 어떤 축을 따라 분할할 수 없고 그것이 주 축이라면, arrangement 뷰는 뷰 하나만 보여 주기로 한다)(12:44)5 프로브는 두 가지를 모두 확인해 주었습니다. 회전한 디스플레이를 채우면 일반 split은 위아래로 쌓였고, 가로 전용 split은 주 창만 보여 주고 다른 것은 아무것도 보여 주지 않았습니다.12 이 앱에서는 인스펙터가 숨겨지므로, 정직한 해결책은 수정자가 아니라 결정입니다. 공간이 폭보다 높이가 크면 compact 레이아웃처럼 인스펙터를 푸시하십시오.

세로로 돌린 내부 디스플레이의 Kiradex. 왼쪽은 가로 내비게이션 바 아래 세트를 카드 네 열로 보여 주고 아래쪽에 떠 있는 탭 바가 있음. 오른쪽은 카드를 선택한 상태로, 위쪽에 카드 한 줄과 두 번째 줄의 윗부분이 가로지르고, 그 아래에 선택한 카드의 스테이지와 세부 정보가 있음

회전한 상태. Apple의 가이드대로 바는 가로이고, 분할은 이 뷰 짝에게 맞지 않는 방향으로 갔습니다.

접힘을 건너 들고 간 카드는 어느 레이아웃에도 들어가지 않습니다. 접은 상태에서 카드를 고르면 그 인스펙터가 내비게이션 스택에 푸시됩니다. 인스펙터를 띄운 채 펼쳐도 카드는 그대로 있습니다. 이 부분은 틀리기 쉬운 곳입니다. 하지만 그것은 여전히 푸시된 화면이고, 이제 폭이 951포인트입니다. 왼쪽에 스테이지, 검은 바탕 위에 떠 있는 흰 상자 안의 세부 정보, 레일에는 뒤로 버튼. 이 디스플레이를 위한 앱의 디자인은 카드 벽과 그 옆의 카드이며, 거기에 닿는 유일한 방법은 뒤로 가서 카드를 다시 고르는 것입니다. 선택 상태 하나가 두 레이아웃을 모두 움직이지만, 내비게이션 경로는 그렇지 않습니다. 고치는 방법은 카드가 선택된 상태에서 compact에서 regular 폭으로 바뀐 것을 감지해 푸시된 화면을 팝하고, 분할이 이어받게 하는 것입니다.

카드 하나를 연 Kiradex의 펼치기 전과 후. 접은 외부 디스플레이에서는 검은 스테이지 위의 카드와 그 아래 세부 정보. 펼친 내부 디스플레이에서는 같은 카드가 검은 화면 왼쪽에 크게 있고, 세부 정보는 오른쪽의 흰 사각형 안에, 레일 위쪽에 뒤로 버튼

펼치기 전과 후의 같은 카드. 상태는 살아남았지만, 레이아웃은 compact 레이아웃을 늘린 것입니다.

regular 폭은 “Duo”가 아닙니다. 이 앱은 regular 폭을 내부 디스플레이로 취급합니다. 벽, 짝수 열, 분할. 가로로 눕힌 6.9인치 iPhone도 regular 폭이며 높이는 compact이고, 이 앱은 방향을 잠그지 않습니다. 거기서 실행하면 같은 분기가 왼쪽 가장자리에서 안으로 들어간 페이지, 카드 이름이 들어가지 않을 만큼 좁은 세부 정보 열을 가진 인스펙터, 화면 아래 가장자리에서 잘린 동작 버튼을 만들어 냅니다. 이것은 iOS 27에서 새로 생긴 문제가 아니며, 제가 돌린 어떤 Duo 포즈에서도 나타나지 않았습니다. Duo 작업이 이것을 찾아낸 것은, regular 폭을 보고하는 모든 화면에서 regular 폭 레이아웃을 누군가 거쳐 본 것이 이번이 처음이었기 때문입니다. 둘을 구분하는 검사는 다른 쪽 size class입니다. 내부 디스플레이는 양쪽 모두 regular이고, Apple의 토크는 옆으로 눕힌 외부 디스플레이를 양쪽 모두 compact로 제시합니다.3 두 방향으로 공간이 필요한 레이아웃이라면 둘 다 물어야 합니다.

카드를 선택한 상태로 가로로 놓인 6.9인치 iPhone의 Kiradex. 카드 페이지는 화면의 약 5분의 1 지점에서 시작하고, 그 옆에 카드 스테이지가 있으며, 오른쪽 세부 정보 패널은 너무 좁아 카드 이름이 세 줄로 나뉘고 Have와 Want 버튼은 화면 아래 가장자리에서 잘림

Duo가 아닌 휴대폰에서의 regular 폭 레이아웃. 옆으로 눕힌 6.9인치 iPhone, iOS 27.0.

키보드가 레일을 가립니다. 외부 디스플레이에서 키보드를 올리면 레일 아래쪽의 탭이 그 뒤로 가려집니다. 이것은 시스템의 레이아웃이지 결함이 아닙니다. 토크는 바가 “as other competing UI appears, like the keyboard”(키보드처럼 경쟁하는 다른 UI가 나타나면서) 오버플로해야 할 수도 있다고 말합니다(11:50).4 그래도 이 목록에 오른 이유는 도구를 망가뜨렸기 때문입니다. 제 첫 투어는 검색 키보드를 올린 채 “Collection”을 탭했고, 그 탭은 P 키에 떨어졌습니다. 탭 바에 항상 닿을 수 있다고 가정하는 UI 테스트는 여기서 가장 먼저 실패합니다.

작은 것 두 가지. 이 앱은 짝수 열을 폭 size class로 정하는데, 토크의 제안은 접힘 자체의 영역이 있는지로 정하는 것입니다. 그리고 접힘과는 상관없는 것 하나. 6.9인치 시뮬레이터에서 전체 화면 뷰어를 네 번 연달아 열었더니, 첫 번째에는 카드가 보였고 나머지 세 번은 빈 검은 화면이었습니다. 둘 다 TestFlight 빌드를 막을 정도는 아닙니다. 둘 다 앱이 화면에 올라가고 무언가가 버튼을 순서대로 누르기 전까지는 보이지 않았습니다.

시뮬레이터가 보여 줄 수 없었던 것

스캐너입니다. 후면 광각 카메라를 열고, 이미 회전 코디네이터를 쓰고 있습니다. 카메라 토크가 요구하는 바로 그것입니다. “On iPhone Duo, the rotation coordinator will update when your app moves displays.”(iPhone Duo에서는 앱이 디스플레이를 옮길 때 회전 코디네이터가 갱신된다)(8:19)6 세션이 한 디스플레이에서 다른 디스플레이로의 이동을 견디는지는 하드웨어로 확인할 문제입니다. 시뮬레이터에는 카메라가 아예 없습니다. Apple의 릴리스 노트는 이 런타임이 실행할 수 없는 것으로 StandBy와 대부분의 앱 익스텐션을 추가하며, Xcode 27.2 beta 2 노트는 캡처를 자동화하는 사람을 위해 하나를 더합니다. “Screenshots and recordings in iPhone Duo may be black for up to a few minutes after booting the device.”(iPhone Duo의 스크린샷과 녹화는 기기 부팅 후 최대 몇 분 동안 검게 나올 수 있다)20

소스 트리 하나, Xcode 두 개

달력이 문제를 만듭니다. 스토어가 받는 빌드는 Xcode 27과 27.0 SDK에서 나오고, iPhone Duo에서 제대로 동작하는 빌드는 27.1 SDK에서 나옵니다. Apple이 스토어를 후자에 열어 주기 전까지는, 업데이트를 출시하면서 Duo 레이아웃 작업도 계속하려는 앱은 두 환경 모두에서 컴파일되어야 합니다.

if #available(iOS 27.1, *)로는 거기까지 가지 못합니다. availability는 실행 시점의 문제이고, 컴파일러는 여전히 심볼을 찾아야 합니다. Kiradex는 Duo 호출 두 개를 정확히 그 방식으로 감싸고 있고, 배포 대상이 27.0이라 아직 업데이트하지 않은 휴대폰에도 빌드가 설치됩니다. 27.1 SDK에서는 이것이 컴파일됩니다. Xcode 27.0에서는 같은 일을 하는 파일이 error: cannot find 'ArrangementView' in scope에서 멈춥니다. 27.0 SDK에는 그런 타입이 없기 때문입니다.14 Kiradex는 아직 스토어에 없으므로 베타 툴체인에 머물러도 됩니다. 고객이 있는 앱은 그럴 수 없습니다.

Swift 문서에 나온 조건으로도 두 SDK를 구분할 수 없습니다. #if compiler(>=6.4)는 양쪽 모두에서 참입니다. Xcode 27.0, Xcode 27.1 beta, Xcode 27.2 beta가 모두 같은 Apple Swift version 6.4 (swiftlang-6.4.0.34.1 clang-2100.3.34.1)을 출력합니다.14 남는 울타리는 두 가지이고, 저는 둘 다 실행해 봤습니다.

직접 설정하는 컴파일 조건. 문서에 나온 방법입니다. 베타로 빌드하는 구성에만 플래그를 정의하고(Xcode에서는 SWIFT_ACTIVE_COMPILATION_CONDITIONS = DUO_SDK, 명령줄에서는 -D DUO_SDK), 27.1 전용 코드를 그것으로 감쌉니다.

struct Pair<Primary: View, Secondary: View>: View {
    @ViewBuilder var primary: Primary
    @ViewBuilder var secondary: Secondary

    var body: some View {
        #if DUO_SDK
        if #available(iOS 27.1, *) {
            ArrangementView { primary } secondary: { secondary }
                .arrangementViewStyle(.split)
        } else {
            HStack(spacing: 0) { primary; secondary }
        }
        #else
        HStack(spacing: 0) { primary; secondary }
        #endif
    }
}

플래그가 없으면 이 파일은 Xcode 27.0에서도 27.1 beta에서도 타입 검사를 통과합니다. 플래그가 있으면 베타에서는 통과하고 27.0에서는 같은 심볼 누락 오류로 실패합니다. 잘못된 조합은 빌드되지 않는다는 뜻입니다.14

컴파일러가 읽는 SDK 자체의 버전. canImport는 모듈 버전과 비교하는, 밑줄이 붙은 두 번째 인자를 받습니다. 그리고 SwiftUI의 모듈 버전은 SDK마다 다릅니다. 27.0 SDK에서는 8.0.84.1.104, 27.1 SDK에서는 8.0.85.27, 27.2 beta SDK에서는 8.1.6.1.101입니다.14 그래서 이 방법에는 빌드 설정이 필요 없습니다.

#if canImport(SwiftUI, _version: 8.0.85)
// ArrangementView, reservedRegions, onHingeChange, toolbarVerticalBehavior
#else
// what the app did before
#endif

각 분기에 #warning을 넣고 세 Xcode 모두로 컴파일해 봤습니다. 27.0은 두 번째 분기를, 27.1 beta와 27.2 beta는 첫 번째 분기를 탔습니다.14 주의할 점은 밑줄에 있습니다. Swift 책에 나온 이 조건의 문법은 canImport(import-path)가 전부이므로, _version:은 문서화된 계약이 없는 컴파일러 기능이고, 8.0.85라는 숫자는 제가 두 SDK에서 읽어 낸 것이지 Apple이 발표한 것이 아닙니다.14 스토어가 27.1에 닫혀 있는 동안 빌드 설정이 여러분의 환경에서 번거롭다면 이것을 쓰고, 근거를 댈 수 있는 방법을 원한다면 플래그를 쓰십시오.

어느 쪽이든 이 울타리는 임시입니다. 27.1 SDK를 탑재한 Xcode의 빌드를 App Store Connect가 받아 주는 날, 울타리를 지우고 #available 검사는 남겨 두십시오. 아직 iOS 27.0에 머무는 휴대폰을 지켜 주는 것은 그쪽입니다.

제출: App Store Connect가 오늘 받는 것

TestFlight는 27.1 빌드를 받습니다. 9월 18일 App Store Connect 릴리스 노트에는 이렇게 적혀 있습니다. “You can now submit apps built with Xcode 27.1 beta using the SDK for iOS 27.1 beta or iPadOS 27.1 beta for internal and external testing.”(iOS 27.1 beta 또는 iPadOS 27.1 beta SDK를 사용해 Xcode 27.1 beta로 빌드한 앱을 내부 및 외부 테스트용으로 제출할 수 있다) 9월 16일과 9월 28일 항목은 Xcode 27.2 beta와 beta 2에 대해 같은 말을 합니다.8 Kiradex는 이 경로로 올라갑니다. 이 글에서 테스트하는 다섯 번째 빌드는 10월 2일에 업로드되었고 App Store Connect는 이를 유효한 빌드로 표시합니다.13

App Store는 아직 받지 않습니다. 스토어를 연 가장 최근 항목은 9월 14일 것입니다. “You can now upload apps built with Xcode 27 using the SDK for iOS 27.0, iPadOS 27.0, macOS 27.0, tvOS 27.0, visionOS 27.0, and watchOS 27.0 for the App Store, and for internal and external testing through TestFlight.”(iOS 27.0 등의 SDK를 사용해 Xcode 27로 빌드한 앱을 App Store용으로, 그리고 TestFlight를 통한 내부 및 외부 테스트용으로 업로드할 수 있다)8 그 이후 어떤 항목도 27.1과 스토어를 한 문장에서 언급하지 않으며, 최신 항목이 9월 28일 자인 Apple의 Releases 피드에 실린 Xcode 27.1 빌드는 9월 18일의 베타 하나뿐입니다.8 이것이 언제 바뀌는지 Apple은 말하지 않았습니다. 기기는 10월 23일에 출시됩니다.1

Apple이 10월 23일 전에 스토어를 27.1 SDK에 열지 않는 한, 출시일에 고객이 갖게 될 스토어 빌드는 기껏해야 27.0 SDK 빌드이며, 그 빌드가 시뮬레이터에서 무엇을 받는지는 위의 비교가 보여 줍니다. Apple의 토크도 같은 가운데 단계를 설명합니다. 상태 레일 옆의 화면, 가로 바, 접힘 없음. 크기 조정이 잘 된다면 쓸 만한 앱이고, 바로 그것이 크기 조정 작업을 지금 Xcode 27로 출시해야 하는 이유입니다.

런치 스크린이 이제 필수입니다. Duo 규칙은 아니지만 같은 SDK와 함께 왔고, 심사가 아니라 업로드 단계에서 실패합니다. Apple의 기술 노트는 이렇게 말합니다. “Starting in iOS 27 and iPadOS 27, App Store Connect requires your app to include a launch screen configuration in its Info.plist,”(iOS 27과 iPadOS 27부터 App Store Connect는 앱의 Info.plist에 런치 스크린 구성이 포함되어 있을 것을 요구한다) 그리고 UILaunchStoryboardName, UILaunchStoryboards, UILaunchScreen, UILaunchScreens 중 어느 것도 없으면 업로드는 “ITMS-90870: Missing launch screen.”으로 거부됩니다.10 Kiradex는 UILaunchScreen을 표지의 빨간색이라는 색상과 함께 선언하므로, 실행되면 곧바로 오프닝 애니메이션으로 이어집니다.13

스크린샷 슬롯은 서류상으로는 존재합니다. App Store Connect의 스크린샷 사양은 9월 9일부터 iPhone Duo를 두 쌍으로 나열하고 있습니다. 외부 디스플레이용 1398 × 2034 또는 2034 × 1398 픽셀, 내부용 2007 × 2853 또는 2853 × 2007 픽셀이며, “Support for uploading assets for this device in App Store Connect will be available later this year.”(App Store Connect에서 이 기기용 에셋 업로드 지원은 올해 후반에 제공될 예정이다)라는 메모가 붙어 있습니다.9 평소의 규칙도 적용됩니다. “You can upload one to 10 screenshots in .jpeg, .jpg, and .png formats,”(.jpeg, .jpg, .png 형식으로 스크린샷을 1장에서 10장까지 업로드할 수 있다) 그리고 “Images can’t include alpha channels or transparencies.”(이미지에 알파 채널이나 투명 영역이 있으면 안 된다)9

업로드 페이지의 한 문장이 이에 대비하는 방식을 결정합니다. “Once your app is submitted for review and approved, you must create a new version to update the screenshots.”(앱이 심사에 제출되어 승인되면, 스크린샷을 업데이트하려면 새 버전을 만들어야 한다)9 Duo 스크린샷은 나중에 따로 끼워 넣을 수 없습니다. 슬롯이 열리면 버전에 실려 나가야 합니다.

종합하면, 제가 권할 순서이자 Kiradex에서 실제로 진행 중인 순서는 이렇습니다.

  1. 지금 27.1 빌드를 TestFlight에 올리고, 거쳐 보며 찾은 문제를 나오는 대로 고칩니다.
  2. 이미 스토어에 있는 앱이라면, 27.1 SDK가 필요 없는 수정을 모두 담아 Xcode 27로 빌드한 업데이트를 냅니다. idiom과 방향 검사 대신 size class, 가장자리별 세이프 에어리어, 시스템 컨테이너가 소유하는 바, 모든 툴바 항목에 제목과 심볼.
  3. Duo 크기의 스크린샷을 지금 시뮬레이터에서 캡처해 보관합니다.
  4. 두 가지가 참이 되는 날을 위해 버전을 준비해 둡니다. 27.1 SDK를 가진 Xcode가 스토어용으로 받아들여지고, Duo 슬롯이 업로드를 받는 날입니다. Apple은 지금까지 이런 종류의 변경을 모두 App Store Connect 릴리스 노트에 게시해 왔습니다. 스토어를 Xcode 27 릴리스에 연 9월 14일 항목과 Duo 스크린샷 사양을 추가한 9월 9일 항목도 그렇습니다. 그러니 지켜볼 페이지는 그곳이고, 그 옆에 developer news 페이지를 두면 됩니다.8

조금 더 먼 곳에 요건이 하나 더 있습니다. 2027년 4월부터는 업로드 자체를 하려면 “iOS and iPadOS apps must be built with the iOS 27 & iPadOS 27 SDK or later”(iOS와 iPadOS 앱은 iOS 27 및 iPadOS 27 SDK 이상으로 빌드해야 한다)입니다.18 여전히 iOS 26 SDK로 빌드한 앱은 Duo에서 위 세 그림 중 가장 작은 것을 받고, 2027년 4월부터는 iOS 27 SDK로 옮기기 전까지 업데이트를 업로드할 수 없습니다.

두 디스플레이를 위한 스크린샷

재료는 이미 거쳐 보는 과정에서 나왔습니다. 모든 정지 지점이 1398 × 2034와 2853 × 2007 픽셀로, 회전한 상태는 2034 × 1398과 2007 × 2853 픽셀로 캡처되어, App Store Connect의 iPhone Duo 행에 있는 네 가지 크기가 모두 갖춰졌습니다.913 남은 일은 그 주위에 무엇을 둘지 정하는 것이고, 접는 휴대폰은 바로 거기서 실수를 부릅니다.

누구나 원하는 그림은 휴대폰이 비스듬히 반쯤 열리고 앱이 접힘을 넘어 쏟아지는 장면입니다. Apple의 마케팅 가이드라인은 그것을 한 줄씩 금지합니다. Apple 자체의 기기 이미지에 대해서는 “Use Apple product images ‘as is’ and without modification. Modifications include adding reflections, shadows, highlights, or graphic elements that appear to enter or come out of the product screen; cropping, tilting, or obstructing any part of the images; animating, flipping, or spinning the images”(Apple 제품 이미지는 ‘있는 그대로’ 수정 없이 사용하라. 수정에는 반사, 그림자, 하이라이트, 제품 화면을 드나드는 듯한 그래픽 요소 추가, 이미지 자르기, 기울이기, 일부 가리기, 애니메이션, 뒤집기, 회전이 포함된다). 직접 만드는 이미지에 대해서는 “Straight-on product shots are preferred. Don’t use extreme angles or alter an Apple product in any way.”(정면 제품 사진이 바람직하다. 극단적인 각도를 쓰거나 Apple 제품을 어떤 식으로든 변경하지 마라). 그리고 Unauthorized Uses 항목의 맨 처음에는 “Rendering in 3D or creating any simulation of an Apple product”(Apple 제품을 3D로 렌더링하거나 어떤 형태로든 시뮬레이션을 만드는 것)가 있습니다.11 제 스크린샷 글은 수상작들이 이 규칙을 어떻게 다루는지, 규칙을 지킨 세트가 어떻게 보이는지 살펴봅니다. 힌지가 있다고 해서 달라지는 것은 없습니다.

대신 Apple이 주는 것은 이 휴대폰용 베젤 팩입니다. 두 가지 마감과 다섯 가지 보기가 있습니다. 세로와 가로의 접힌 휴대폰, 가로와 세로의 펼친 내부 디스플레이, 그리고 카메라 옆에 외부 디스플레이가 보이는, 뒤에서 본 펼친 휴대폰입니다. 이 파일들의 개구부는 스크린샷 크기와 정확히 같아서, 시뮬레이터 캡처를 크기 조정 없이 끼워 넣을 수 있습니다.11 반쯤 접힌 보기도, 비스듬한 보기도 없습니다.

그래서 아래 프레임은 세 부분으로 만들었습니다. 앱 고유의 색으로 된 바탕, 짧은 한 줄의 글, 그리고 Apple 베젤 안에 온전하고 똑바로, 위에 아무것도 얹지 않은 채 들어간 캡처입니다. 변화는 바탕, 기기의 크기, 그리고 세트마다 하나씩 있는 기기 없는 프레임에서 나옵니다. 검은 바탕 위에 앱의 전체 화면 카드를 놓은 것으로, 앱 자신의 3D이지 누구의 하드웨어도 아닙니다.

Kiradex의 App Store 스크린샷 세 줄. 6.9인치 iPhone용 프레임 여섯 개, iPhone Duo 외부 디스플레이용 다섯 개, 가로 방향 내부 디스플레이용 다섯 개. 각 프레임에는 Your collection. In your hands. 또는 Open it. The binder fills the wall. 같은 짧은 헤드라인, 빨강, 초록 또는 어두운 바탕, 그리고 정면을 향한 Apple 기기 베젤 안의 앱 화면이 있음. 첫째 줄과 셋째 줄에는 Turn it over.라는 헤드라인과 함께 검은 바탕 위에 트레이딩 카드 한 장만 놓인 프레임이 하나씩 있음

투어 캡처로 스크립트가 구성한 세 세트, 1320 × 2868, 1398 × 2034, 2853 × 2007 픽셀. 방법을 보여 주는 것이지 실제 등록물이 아닙니다. 이 캡처에는 카탈로그 카드가 담겨 있으며, 스토어 세트에 무엇을 보여 줄지는 아래 세 번째 메모에서 다루는 미해결 문제입니다.

만들면서 얻은 실용적인 메모 세 가지입니다.

스크립트로 구성하십시오. 위 세트는 캡처, 헤드라인, 바탕을 받아 알파 채널 없이 정확한 크기의 PNG를 써 내는 Python 파일 하나에서 나옵니다. 앱이 바뀌면(이 앱은 캡처한 당일에 두 번 바뀌었습니다) 투어를 다시 돌리면 프레임이 새로 만들어집니다.

휴대폰이 더해 주는 것을 스크린샷으로 담으십시오. Apple의 업로드 페이지는 모든 크기에서 인터페이스가 같다면 6.9인치 세트로 충분하다고 말합니다. “provide only the highest resolution screenshots required. They automatically scale down to smaller device sizes.”(필요한 최고 해상도 스크린샷만 제공하라. 더 작은 기기 크기로 자동 축소된다)9 이 휴대폰에서는 같지 않습니다. 카드 벽과 분할된 인스펙터는 내부 디스플레이에만 존재합니다. 내부 세트를 여는 두 프레임이 바로 그것입니다.

화면에 무엇이 있는지 신경 쓰십시오. 같은 가이드라인은 “You are responsible for securing the rights to all materials used in screen content within your app.”(앱의 화면 콘텐츠에 사용된 모든 자료의 권리를 확보할 책임은 당신에게 있다)라고 말합니다.11 수집가용 앱은 본질적으로 다른 사람의 아트워크를 보여 주고, 스토어 등록은 마케팅이며, 이는 앱이 사용 중에 표시하는 것과는 다른 문제입니다. Kiradex에 대해서는 아직 그 결정을 내리지 않았고, 위의 프레임을 그대로 제출하지는 않을 것입니다. 이런 이유로 투어에는 가상의 카드 여섯 장으로 돌아가는 두 번째 모드가 있습니다. 다만 그 카드에는 아직 아트워크가 없으므로, 제출할 세트를 만들려면 대체 이미지를 디자인하거나 권리 문제를 정리해야 합니다.

Kiradex에는 아직 등록 페이지가 없습니다. 등록하게 되면 6.9인치 세트로 시작하고, Duo 세트는 App Store Connect가 받아 주는 날을 위해 별도의 버전과 함께 대기합니다.

코딩 에이전트에게 일을 넘기기

이 작업의 대부분은 에이전트가 잘하는 종류의 일입니다. 단, 에이전트가 앱을 볼 수 있다면 말입니다. 작업은 두 절반으로 나뉘고, 그중 하나는 Apple이 제공합니다.

정적인 절반은 Apple의 것입니다. 첫 번째 Tech Talk가 이 이야기로 끝납니다. “During the talk, Modernize Your UIKit App, we introduced a new app modernization skill. With Xcode 27.1, this skill has a new name: App Resizability. It now supports SwiftUI and iPhone Duo.”(Modernize Your UIKit App 토크에서 새로운 앱 현대화 스킬을 소개했다. Xcode 27.1에서 이 스킬은 App Resizability라는 새 이름을 얻었고, 이제 SwiftUI와 iPhone Duo를 지원한다)(9:19)3 이 스킬은 베타 안에 든 일반 텍스트 파일 묶음으로, Xcode 27.1 beta에서는 Contents/PlugIns/IDEIntelligenceChat.framework/Versions/A/Resources/app-resizability.idechatprompttemplate과 그 옆의 참조 파일 다섯 개입니다.15 그 지시문은 그물을 얼마나 넓게 칠지 말합니다. “Treat a request about the foldable iPhone Duo as a request for every task in the Task Registry, because a screen that changes size exposes all of them at once.”(접이식 iPhone Duo에 관한 요청은 Task Registry의 모든 작업에 대한 요청으로 취급하라. 크기가 바뀌는 화면은 그 모두를 한꺼번에 드러내기 때문이다)15

실행하지 않더라도 읽어 볼 가치가 있습니다. Apple의 리뷰 체크리스트를 글로 적어 둔 것이기 때문입니다. 프로젝트 설정 세 가지를 확인하되 어느 것도 바꾸지 않고(런치 스크린 키, iPad 방향 선언, UIRequiresFullScreen), 다섯 가지 패턴을 찾습니다. UIScreen.main, 인터페이스 방향으로 정하는 레이아웃, userInterfaceIdiom으로 정하는 레이아웃, 씬 생명주기가 있어야 할 곳의 애플리케이션 생명주기, 그리고 양쪽이 같다고 가정하는 세이프 에어리어 코드입니다. 세이프 에어리어 파일에는 이 휴대폰에서 잘못되는 일의 절반을 설명하는 줄이 있습니다. “Asymmetric horizontal insets are the normal case. Where a vertical bar is present, one horizontal edge usually carries the whole inset and the other carries zero.”(좌우 비대칭 인셋이 정상적인 경우다. 세로 바가 있으면 보통 한쪽 가장자리가 인셋 전체를, 다른 쪽은 0을 갖는다)15

Kiradex는 올해 작성한 SwiftUI 앱이고, 그 다섯 가지 검색은 아무것도 찾지 못합니다.13 위의 발견은 모두 앱을 실행해서 나왔습니다. 이것이 정적인 절반의 한계입니다. 정적 분석은 소스를 읽지만, 접힘, 레일, 키보드는 화면 위에서만 보입니다.

나머지 절반은 에이전트가 실행하고 눈으로 볼 수 있는 순회입니다. 위의 감사를 가능하게 한 것은 작은 부품 세 개였고, 어느 것도 이 앱에 국한되지 않습니다.

  1. 모든 종류의 화면을 방문해 각각에서 멈추는 UI 테스트. 단언이 아니라 투어입니다. 저희 것은 Dex, 세트, 카드, 시트, 컬렉션 목록, 컬렉션의 카드, 전체 화면 뷰어, 키보드를 올린 검색을 엽니다. 정지 지점은 여덟 개입니다.13
  2. 테스트 안이 아니라 Mac에서 하는 캡처. UI 테스트 자체의 스크린샷은 디스플레이 하나만 찍습니다. simctl은 둘 다에 닿습니다.
xcrun simctl io "$UDID" screenshot --type=png --display=primary   outer.png
xcrun simctl io "$UDID" screenshot --type=png --display=primary-1 inner.png

테스트는 정지 지점 이름의 빈 파일을 쓰고, Mac의 셸 루프가 그것을 보고 두 디스플레이를 캡처한 뒤 두 번째 파일로 응답합니다. 테스트는 그것을 기다렸다가 다음으로 넘어갑니다. 외부 캡처는 휴대폰을 접은 상태에서 1398 × 2034 픽셀, 내부 캡처는 펼친 상태에서 2853 × 2007 픽셀이므로, 감사용 그림과 스토어 스크린샷이 같은 실행에서 나옵니다.913 3. 마우스에 손을 대지 않고 포즈를 바꾸는 방법. simctl에는 포즈 명령이 없고, 이 베타의 UI 테스트 프레임워크에도 제가 찾을 수 있는 한 힌지 호출이 없습니다. 그래서 스크립트는 Device Hub의 Closed, Book, Open, Rotate Right 버튼을 손쉬운 사용(접근성) API로 누르며, 이는 Device Hub가 백그라운드에 있어도 동작합니다.13

이것들이 있으면 모든 포즈에서 앱을 점검하는 일은 에이전트의 의견을 구하는 일이 아니라, 에이전트도 여러분도 읽을 수 있는 포즈별 이미지 폴더가 됩니다. 아래가 제가 넘길 브리프입니다. 그대로 붙여 넣을 수 있게 썼습니다.

Goal: make this app correct on iPhone Duo in every pose, without device checks.

0. Toolchain. Build with Xcode 27.1 beta. Confirm the binary is stamped with the 27.1 SDK:
   otool -l <App>.app/<App> | grep -A4 LC_BUILD_VERSION      (debug builds: <App>.debug.dylib)
   If "sdk" is lower than 27.1, stop: nothing below will reproduce.

1. Static pass. Report every use, with file and line, of:
   UIScreen.main / UIScreen.mainScreen; userInterfaceIdiom; interface orientation used for layout;
   a UIApplicationDelegate doing scene work; one safe-area inset applied to both sides;
   a bare ignoresSafeArea() on anything a person reads or taps; fixed widths tied to a phone size;
   toolbars or tab bars built by hand instead of owned by NavigationStack, NavigationSplitView or TabView;
   toolbar items with a title and no symbol, or a symbol and no title.
   Confirm Info.plist has a launch screen key and no orientation lock the design does not need.

2. Walk. Run the screenshot tour in each pose and capture BOTH displays at every stop:
   closed; closed and turned; open; open and turned; partly folded; partly folded and turned.
   If no tour exists, write a UI test that stops at each kind of screen and signals a host script, and have
   the script capture with:  xcrun simctl io <udid> screenshot --display=primary     (outer)
                             xcrun simctl io <udid> screenshot --display=primary-1   (inner)
   Poses are buttons in Device Hub (Closed, Book, Open, Rotate Right); simctl has no pose command.

3. Read every capture and answer, per pose:
   - Are the bars where the system puts them (down the side closed and in open landscape, across the top
     and bottom in open portrait)? Is any toolbar item missing from the bar and from the overflow menu?
   - Does any sheet reserve the side rail and leave it empty?
   - With the keyboard up, what is covered? Can every tab still be reached once it is dismissed?
   - Does anything sit under the outer camera corner or the status column?
   - Open: does a grid have an even number of columns? Does any control or line of text cross the middle?
   - Partly folded: does content move off the fold? Do sheets and alerts land on one side?
   - Turned: did a two-pane layout stack when it should have stayed side by side, or the reverse?
   - Is the same selection, scroll position and navigation path still there after the pose changed?

4. Fix with, in this order: a system container that already adapts; size classes; ArrangementView for a
   custom two-pane layout; reservedRegions for hand-placed content. Never branch on the device model,
   the idiom, the orientation, or the raw hinge angle to decide layout.

5. Guard everything from the 27.1 SDK with `if #available(iOS 27.1, *)` and a fallback that keeps both
   panes reachable. If the same source must also build with Xcode 27.0, fence it at compile time.

6. Report what could not be checked in the simulator: cameras, StandBy, extensions, haptics, real reach.

Apple의 자료를 제가 쓸 순서대로

Apple이 이 기기에 대해 공개한 모든 자료는 한 페이지, Get ready for iPhone Duo에서 이어집니다.19 작업 순서대로 정리하면 이렇습니다.

자료 여는 이유
Prepare your app for iPhone Duo(Tech Talk) SDK 세 단계, size class, 세이프 에어리어. 여기서 시작
Preparing your app for iPhone Duo(가이드) 같은 내용을 글로, 모든 API 이름과 함께. “Address common layout and resizing considerations” 아래의 네 가지 점검이 감사 목록2
Raise the bar with iPhone Duo 세로 바. 무엇이 들어가고 무엇이 빠지는지, 오버플로
Strike a pose with adaptive layouts on iPhone Duo 접힘. 비켜 놓기, 예약 영역, arrangement 뷰
Designing for iPhone Duo(HIG)와 Design for iPhone Duo 디자이너가 여러분에게 지키라고 할 규칙7
Leverage multiple displays and scenes on iPhone Duo 힌지, Split View, 외부 디스플레이의 두 번째 씬
Build a great camera experience for iPhone Duo 촬영하는 앱만. 전면 카메라 두 개와 각각이 향하는 방향6
Group Lab 녹화본 1일 차와 2일 차, SwiftUI, UIKit, Photos and Camera 포럼 Q&A Apple 엔지니어가 개발자의 질문에 답함19
Apple Design Resources Figma와 Sketch 키트, 마케팅용 제품 베젤11
Apple 외부: SwiftLee의 시뮬레이터 가이드, iPhone Duo by Examples, BleepingSwift의 체크리스트, Adapty의 가이드 테스트 목록과 힌지 슬라이더. API마다 실행 가능한 예제 하나와 현장 노트. 짧은 체크리스트. 페이월까지 다루는, 제가 찾은 유일한 자료17
워크숍 대면. SwiftLee는 “with the opportunity to test your app on a physical device,”(실제 기기에서 앱을 테스트할 기회와 함께)라고 설명하며, 10월 23일 전에 카메라를 확인할 유일한 방법1719

Group Lab 질의응답 정리본과 포럼 스레드는 읽지 않았습니다. Apple 포럼 페이지는 자동화된 요청에 사람 확인 페이지로 응답하며, 저는 그것을 우회하지 않았습니다.

핵심 정리

  • 지금 앱을 출시하고 있다면: 크기 조정 작업을 지금 Xcode 27로 빌드해 내보내십시오. 10월 23일에 스토어가 여전히 27.1 SDK에 닫혀 있다면 고객이 Duo에서 갖게 될 것이 바로 그것이고, 그 어느 것도 베타가 필요 없습니다.
  • Duo API를 채택한다면: Xcode 27.1 beta로 빌드하고, otool로 sdk 27.1 이상인지 확인하고, 빌드는 TestFlight에 두고, 같은 소스를 여전히 스토어용으로도 빌드해야 한다면 27.1 전용 호출을 울타리로 감싸십시오.
  • 테스트한다면: 코드를 읽지 말고 포즈를 돌려 보십시오. 접은 상태, 펼친 상태, 반쯤 접은 상태, 각각을 회전한 상태, 시트, 키보드, 전체 화면 뷰, 그리고 접힘을 건너 들고 간 화면 하나. 매번 두 디스플레이를 모두 캡처하십시오.
  • 스토어 등록물을 만든다면: 지금 Duo 크기로 캡처하고, 스크립트로 구성하고, Apple 베젤을 정면으로 쓰고, 슬롯이 열리는 날을 위해 버전을 준비해 두십시오.
  • 이 일을 에이전트에게 넘긴다면: 파일만이 아니라 순회를 넘기십시오. Apple의 App Resizability 스킬은 읽어서 찾을 수 있는 것을 다룹니다. 위의 발견은 모두 보아서 찾은 것입니다.

자주 묻는 질문

기존 앱은 변경 없이 iPhone Duo에서 실행됩니까?

그렇습니다. Apple의 토크는 어떤 SDK로 빌드한 앱에도 이를 약속하며, 시뮬레이터도 이를 뒷받침합니다. iOS 26 SDK로 찍힌 빌드는 375 × 667 포인트 창으로 실행되었고, 27.0으로 찍힌 빌드는 상태 레일 옆의 화면을 가로 바와 함께 채웠습니다. 둘 다 세로 바나 예약 영역은 받지 못합니다.312

iPhone Duo의 완전한 레이아웃에는 어떤 Xcode가 필요합니까?

Xcode 27.1 beta(27A9269)입니다. iOS 27.1 SDK와 유일한 iPhone Duo 시뮬레이터를 탑재하고 있습니다. 이 버전의 27.1 시뮬레이터 런타임은 iPhone Duo 기기 유형만 지원하므로, 다른 iPhone은 27.0 런타임에서 테스트합니다. Xcode 27.2 beta의 SDK도 같은 API를 갖고 있고, 27.2로 찍힌 프로브는 시뮬레이터에서 27.1과 똑같이 동작했지만, 그 Xcode에는 실행할 Duo가 없습니다.81214

지금 iPhone Duo 빌드를 App Store에 제출할 수 있습니까?

2026년 10월 2일 현재, 27.1 SDK로 빌드한 것은 제출할 수 없습니다. App Store Connect는 Xcode 27.1 beta와 27.2 beta의 빌드를 내부 및 외부 TestFlight 테스트용으로, Xcode 27 빌드를 스토어용으로 받습니다. Apple은 이 변경의 날짜를 밝히지 않았습니다.8

App Store Connect는 iPhone Duo용으로 어떤 스크린샷 크기를 요구합니까?

외부 디스플레이용 1398 × 2034 또는 2034 × 1398 픽셀, 내부 디스플레이용 2007 × 2853 또는 2853 × 2007 픽셀이며, 알파 채널 없는 이미지 1장에서 10장입니다. 업로드는 “later this year”(올해 후반) 제공 예정으로 나와 있습니다.9

iPhone Duo 시뮬레이터의 두 디스플레이를 어떻게 캡처합니까?

외부 디스플레이는 xcrun simctl io <udid> screenshot --display=primary, 내부 디스플레이는 --display=primary-1입니다. UI 테스트 자체의 스크린샷은 디스플레이 하나만 담습니다.13

iPhone Duo 시뮬레이터에서 앱의 바가 여전히 가로인 이유는 무엇입니까?

원인은 세 가지이며, 확인할 순서대로 적습니다. 바이너리가 27.1 SDK로 찍히지 않았습니다. 바가 TabView, NavigationStack, NavigationSplitView의 소유가 아니라 직접 만든 것입니다. 또는 내부 디스플레이가 세로 방향이고, 그곳에서는 Apple이 설계상 바를 가로로 유지합니다.2712

포즈마다 레이아웃이 필요합니까?

아닙니다. Apple의 가이드는 compact 폭과 regular 폭의 두 레이아웃에, 콘텐츠가 접힘을 가로지를 만한 곳에서 접힘에 대응하는 것을 더하라는 것입니다. Kiradex에는 그 두 레이아웃과 arrangement 뷰 하나가 있고, Book 포즈에는 코드가 필요 없었습니다.713

이 사이트의 관련 글: 시뮬레이터 글에는 설치 단계, 기기 프로파일, 정정된 포즈별 측정값이 있습니다. iPhone Duo를 위한 디자인은 Apple의 디자인 가이드를 규칙으로 읽어 냅니다. Duo 개발자 글에는 하드웨어 수치와 날짜별 경과가 있습니다. 스크린샷 글은 세트가 무엇을 말해야 하는지에 대한 논증입니다. 크기 조정 가능한 iPhone 체크리스트는 이 글이 먼저 출시하라고 권하는 Xcode 27 작업입니다. 그리고 Xcode 27 허브가 툴체인을 추적합니다.

출처


  1. Apple Newsroom, Apple unveils iPhone Duo, 2026년 9월 9일, 2026년 10월 2일 확인: “Pre-orders begin Friday, October 16, with availability beginning Friday, October 23.”(사전 주문은 10월 16일 금요일에 시작되고 출시는 10월 23일 금요일부터) 및 “iPhone Duo will be available with iOS 27.1.”(iPhone Duo는 iOS 27.1과 함께 출시된다) ↩↩↩

  2. Apple, Preparing your app for iPhone Duo, Technology Overviews, 2026년 10월 2일 문서 JSON 엔드포인트로 확인. Overview와 “Address common layout and resizing considerations”, “Organize items in your bars”, “Arrange views in different poses” 섹션을 인용. Xcode 버전에 관한 Overview 문장은 이 사이트가 2026년 9월 17일에 인용할 때는 달랐습니다(“Build your app with Xcode 27.1 or later to use all of the available screen space on iPhone Duo. In earlier versions, your app doesn’t extend under the status bar and camera.”). 현재 문구는 이 사이트의 Duo 개발자 글이 9월 19일에 기록했습니다. 페이지에는 개정 메모가 없습니다. ↩↩↩↩↩↩

  3. Apple Developer Tech Talk, Prepare your app for iPhone Duo, David Jackson, UI Frameworks. 인용은 표시한 시각의 토크 영어 자막 트랙에서 가져왔습니다. ↩↩↩↩

  4. Apple Developer Tech Talk, Raise the bar with iPhone Duo, Anna(UI Frameworks 엔지니어)와 Maria(Human Interface Designer). 인용은 표시한 시각의 토크 영어 자막 트랙에서 가져왔습니다. ↩↩↩

  5. Apple Developer Tech Talk, Strike a pose with adaptive layouts on iPhone Duo, Maria(Human Interface Designer)와 Harry(UI Frameworks 엔지니어). 인용은 표시한 시각의 토크 영어 자막 트랙에서 가져왔습니다. ↩↩↩

  6. Apple Developer Tech Talks, Leverage multiple displays and scenes on iPhone Duo와 Build a great camera experience for iPhone Duo, 인용은 표시한 시각의 영어 자막 트랙에서. Apple, Choosing a camera by the direction it faces, AVKit, 2026년 10월 2일 확인. ↩↩↩

  7. Apple, Designing for iPhone Duo, Human Interface Guidelines, 2026년 10월 2일 문서 JSON 엔드포인트로 확인. “Device poses”와 “Vertical controls” 섹션을 인용. ↩↩↩↩↩

  8. Apple, App Store Connect release notes, 2026년 10월 2일 확인: 2026년 9월 14일과 9월 18일 자 항목을 인용. 9월 9일 자 항목은 iPhone Duo 스크린샷 사양을 추가하면서 업로드가 “will be available later this year”(올해 후반에 제공될 예정)라고 하며, 사양 페이지가 이 문장을 되풀이합니다. 9월 16일과 9월 28일 자 항목은 같은 형식으로 여섯 플랫폼에 대해 TestFlight를 Xcode 27.2 beta와 beta 2에 엽니다. 9월 18일 이후 날짜의 어떤 항목도 Xcode 27.1이나 iOS 27.1 SDK를 언급하지 않습니다. Apple, Releases, RSS 피드, 2026년 10월 2일 확인, 마지막 빌드 날짜 Mon, 28 Sep 2026 14:00:00 PDT: Xcode 27.1을 언급하는 항목은 “Xcode 27.1 beta (27A9269)” 하나로 날짜는 Fri, 18 Sep 2026이며, 가장 최근 Xcode 항목은 “Xcode 27.2 beta 2 (27B5028f)”로 날짜는 Mon, 28 Sep 2026입니다. ↩↩↩↩↩↩↩↩↩↩↩

  9. Apple, Screenshot specifications와 Upload app previews and screenshots, App Store Connect Help, 2026년 10월 2일 확인, 인용. ↩↩↩↩↩↩↩↩↩↩

  10. Apple, TN3208: Preparing your app’s launch screen to meet App Store requirements, 2026년 10월 2일 확인, 인용. 개정 이력: “2026-09-14 Updated the ITMS-90870 error message to reflect the iOS 27 launch screen requirement.”(iOS 27 런치 스크린 요건을 반영해 ITMS-90870 오류 메시지 갱신) 및 “2026-06-08 First published.”(최초 게시) ↩↩↩

  11. Apple, Marketing Resources and Identity Guidelines, “Apple Product Images”, “Unauthorized Uses”, “Screen Content”, “Custom Photography and Video” 섹션, 2026년 10월 2일 확인, 인용. Apple, Apple Design Resources, Product Bezels, iPhone Duo(Photoshop 및 PNG), 2026년 10월 2일 확인. 베젤 치수는 Apple의 Bezel-iPhone-Duo.dmg에 든 PNG 파일에서 필자가 측정한 것입니다. “Outer Closed Portrait”는 1574 × 2194 픽셀이고 투명한 개구부는 1398 × 2034, “Inner Open Landscape”는 3093 × 2247이고 개구부는 2853 × 2007입니다. 팩에는 “Inner Open Portrait”, “Outer Closed Landscape”, “Outer Open”도 있으며, 각각 Star White와 Night Sky 두 가지입니다. “Outer Closed Portrait”의 카메라 컷아웃은 개구부 안에서 1포인트당 3픽셀로 환산해 측정하면 가로 400.3에서 436.3포인트, 세로 29.7에서 65.7포인트에 걸칩니다. 프로브의 카메라 occlusion은 399에서 436, 30에서 67입니다. ↩↩↩↩↩↩

  12. 2026년 10월 2일 필자의 실행: macOS 27.0(26A428), Xcode 27.1 beta(27A9269), iOS 27.1 시뮬레이터 런타임(24A94401), iPhone Duo 기기 유형으로 만든 시뮬레이터. 프로브 DuoProbe2는 Swift 파일 하나입니다. 첫 탭에 툴바 항목 다섯 개를 가진 NavigationStack을 담은 TabView, GeometryProxy 크기, size class, toolbarVerticalEdge, onHingeChange, 그리고 .includeInactive를 붙인 두 종류의 reservedRegions를 보여 주는 판독 화면(body가 평가될 때마다 읽고 1초마다 다시 평가), split 스타일의 ArrangementView, 그리고 창, 창 씬의 화면, UIScreen.main, verticalBarEdge 트레이트를 로그로 남기는 UIKit 뷰로 이루어집니다. 이 파일을 27.1 시뮬레이터 SDK에 대해 swiftc로, 배포 대상 27.1, 기록할 SDK 버전을 링커에 지정해(-Xlinker -platform_version -Xlinker ios-simulator -Xlinker 27.1 -Xlinker <26.0, 27.0 or 27.1>) 세 번 컴파일했고, 각 바이너리에 대한 otool -l은 해당 sdk 값을 출력했습니다. 포즈는 Device Hub의 Closed, Book, Open, Rotate Right 버튼을 손쉬운 사용(접근성) API로 눌러 설정했습니다. 콘솔 줄, 27.1 스탬프, 접은 상태: size=382x562 ... h=compact v=regular verticalEdge=trailing safe=EdgeInsets(top: 82.0, leading: 0.0, bottom: 34.0, trailing: 84.0) divisions=0 occlusions=2, occlusion[0] active=true frame=(399,-52 37x37), occlusion[1] active=true frame=(382,-82 84x170), window=(466.0, 678.0), verticalBarEdge=2. 펼친 상태: size=867x553 ... h=regular v=regular verticalEdge=trailing ... divisions=1 occlusions=2, division[0] active=false frame=(455,-82 40x669) margins=EdgeInsets(top: 0.0, leading: 20.0, bottom: 0.0, trailing: 20.0), occlusion[0] active=false frame=(677,-61 58x37), occlusion[1] active=true frame=(867,-82 84x120), window=(951.0, 669.0), hinge=fullyOpen 180 deg. Book: 같되 division[0] active=true와 hinge=partiallyOpen 127 deg. 펼쳐서 회전: size=669x734 ... verticalEdge=nil ... divisions=1 occlusions=2, division[0] active=false frame=(0,321 669x40), window=(669.0, 951.0), mainScreen=(466.0, 678.0), verticalBarEdge=0. 접어서 회전: size=594x350 ... h=compact v=compact verticalEdge=trailing, window=(678.0, 466.0). 프레임은 판독 뷰의 좌표계이며, 그 원점은 창 위쪽에서 82 또는 134포인트 아래에 있습니다. 27.0 스탬프: 접은 상태 window=(386.0, 678.0), 펼친 상태 (871.0, 669.0), 펼쳐서 회전 (669.0, 871.0), 접어서 회전 (678.0, 386.0), 모든 포즈의 모든 줄에서 verticalEdge=nil과 divisions=0 occlusions=0. 26.0 스탬프: 접은 상태, 펼친 상태, Book, 펼쳐서 회전 모두 window=(375.0, 667.0), 접어서 회전 (667.0, 375.0), 전체적으로 h=compact. 27.1로 실행할 때마다 처음 6번에서 9번의 평가는 영역이 나타나기 전에 divisions=0 occlusions=0을 기록했고, 그중 세 번은 뷰가 크기를 갖기 전이었습니다. Open과 Book 포즈의 창 위치는 캡처에서 측정했습니다. 평평할 때 주 창은 x 8에서 433.7, 보조 창은 433.7에서 859. Book에서는 주 창이 455.7까지, 빈 띠가 495.7까지, 보조 창이 859까지. 시트, 접은 상태: 텍스트만 있는 Done에서 콘텐츠 374x562, Done에 심볼을 달아도 같고, .toolbarVerticalBehavior(.disabled)에서는 450x428과 verticalEdge=nil. 펼친 상태: 가운데 653x501, h=compact. Book: leading 가장자리에 459x501. arrangement 뷰가 콘텐츠 영역을 채운 상태(실행 옵션)에서 일반 .split은 회전한 디스플레이에서 주 창을 보조 창 위에 두었고, .split.axes(.horizontal)은 주 창만 보여 주었습니다. 펼쳐서 평평할 때 같은 뷰는 좌우로 분할했고, Book에서는 접힘의 띠를 비워 두었습니다. 9월 21일 글이 설명하는 방식(SDKROOT를 설정하지 않은 swiftc -sdk)으로 컴파일한 프로브는 clang: warning: using sysroot for 'macOS 27.0' but targeting 'arm64-apple-ios27.1.0-simulator'을 출력했고 sdk 27.0으로 찍혔습니다. 추가로 두 빌드를 접은 상태와 펼친 상태에서 실행했습니다. PlainProbe는 같은 탭 뷰, 내비게이션 스택, 툴바를 가지되 27.1 SDK의 요소는 하나도 없는 것으로, Xcode 27.0(27A266a)이 자체 iOS 27.0 SDK에 대해 컴파일했습니다(otool: minos 27.0, sdk 27.0). 접은 상태 window=(386.0, 678.0), 펼친 상태 (871.0, 669.0). sdk 27.2로 찍은 DuoProbe2: 두 포즈 모두 27.1 스탬프와 같은 줄. simctl list runtimes -j는 27.1 런타임의 supportedDeviceTypes를 iPhone Duo 한 항목으로 보여 주며, 그 런타임에서 iPhone 18 Pro Max 유형으로 simctl create를 실행하면 “Incompatible device”로 실패합니다. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩

  13. Kiradex는 필자의 앱(941 Apps)으로, 번들 버전 1.0 빌드 5이며 2026년 10월 2일 TestFlight에 업로드되었고 같은 날 App Store Connect에서 유효로 표시되었습니다. 프로젝트 관련 사실은 해당 빌드 시점의 소스에서 가져왔습니다. 배포 대상 iOS 27.0, 27.1 SDK로 빌드(앱 바이너리에 대한 otool -l: minos 27.0, sdk 27.1), 색상이 지정된 UILaunchScreen, 세로와 양쪽 가로 방향, 다섯 탭을 가진 TabView, 그리고 #available(iOS 27.1, *) 두 곳으로, 하나는 ArrangementView를, 하나는 onHingeChange를 감쌉니다. 로드맵 문장은 프로젝트 자체 노트에서 인용했습니다. 순회는 여덟 종류의 화면에서 멈추고 호스트 스크립트에 simctl io <udid> screenshot --display=primary와 --display=primary-1로 시뮬레이터 캡처를 요청하는 UI 테스트입니다. iPhone Duo 시뮬레이터(iOS 27.1 런타임)에서 여섯 포즈로 실행했고, 그중 다섯 포즈에서는 여덟 정지 지점 전부, 접어서 회전한 상태에서는 일곱 개였으며, iPhone 18 Pro Max 시뮬레이터(iOS 27.0 런타임, 세로와 가로)에서도 실행했습니다. 캡처 크기: 1398 × 2034와 2034 × 1398(외부), 2853 × 2007과 2007 × 2853(내부), 1320 × 2868(6.9인치). 인스펙터의 왼쪽 가장자리는 내부 디스플레이 캡처에서 측정해 평평할 때 x 433.7, Book 포즈에서 495.7이었습니다. 펼치기 테스트는 앱을 접은 상태로 실행하고 내부 디스플레이를 연속으로 캡처하면서 Open을 눌렀으며, 연속된 네 프레임에 오프닝이 담겼습니다. 카드 들고 가기 테스트는 접은 상태에서 카드를 열고 Open을 누른 뒤 20초 후에 캡처했습니다. 뷰어 테스트는 6.9인치 시뮬레이터에서 한 세션 동안 전체 화면 뷰어를 네 번 열고 각 캡처를 측정했습니다. 첫 번째는 검지 않은 픽셀이 52퍼센트, 나머지 세 번은 0.3퍼센트였습니다. Xcode 27.1 beta의 시뮬레이터 플랫폼에 있는 XCTest와 XCUIAutomation 프레임워크를 “hinge”, “posture”, “DevicePose”, “foldState”로 텍스트 검색했지만 아무것도 나오지 않았고, simctl에도 포즈 명령은 없습니다. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩

  14. 2026년 10월 2일 필자의 테스트. if #available(iOS 27.1, *) 뒤에서 ArrangementView를 쓰는 파일을 설치된 각 Xcode의 시뮬레이터 SDK에 대해 swiftc -typecheck로 타입 검사했습니다. Xcode 27.0(27A266a)은 error: cannot find 'ArrangementView' in scope로 실패했고, Xcode 27.1 beta(27A9269)와 Xcode 27.2 beta(27B5019j)는 통과했습니다. 같은 파일을 #if DUO_SDK로 감싼 것은 27.0에서 플래그 없이 통과했고, 27.1 beta에서는 플래그 유무와 관계없이 통과했으며, 27.0에서 -D DUO_SDK를 주면 실패했습니다. #if canImport(SwiftUI, _version: 8.0.85)로 감싼 것은 세 버전 모두에서 통과했고, 각 분기에 넣은 #warning은 27.0이 대체 경로를, 두 베타가 27.1 분기를 컴파일했음을 보여 주었습니다. xcrun swift --version은 세 버전 모두에서 Apple Swift version 6.4 (swiftlang-6.4.0.34.1 clang-2100.3.34.1)을 출력합니다. SwiftUI 모듈 버전은 각 SDK의 SwiftUI.swiftinterface에 있는 -user-module-version 값입니다. 여기 설치된 27.2 beta는 beta 1(27B5019j)입니다. 그 iOS 27.2 시뮬레이터 런타임(24B5084k)은 iPhone Duo 기기 유형을 나열하지 않으며, Apple의 27.2 노트는 그것을 위해 개발자에게 27.1 beta를 안내합니다. 이 조건의 문법은 The Swift Programming Language의 Statements, “Conditional Compilation Block”, 2026년 10월 2일 확인에서 가져왔습니다: “platform-condition → canImport ( import-path )”. ↩↩↩↩↩↩↩↩↩

  15. Xcode 27.1 beta(27A9269), Contents/PlugIns/IDEIntelligenceChat.framework/Versions/A/Resources/: app-resizability.idechatprompttemplate과 app-resizability-ref-uiscreen-task.md.packaged, -orientation-task, -scene-lifecycle-task, -safe-area-task, -idiom-task, 2026년 10월 2일에 읽음. 인용은 템플릿의 “When to Use” 섹션과 세이프 에어리어 참조 파일의 규칙 6에서 가져왔으며, 세 가지 프로젝트 점검은 그 “Prerequisites” 표, 다섯 가지 패턴은 “Task Registry”입니다. Xcode 27.0(27A266a)의 같은 폴더에는 uikit-app-modernization.idechatprompttemplate과 참조 파일 네 개가 있습니다. ↩↩↩

  16. Apple, 2026년 10월 2일 JSON 엔드포인트로 확인한 문서: ArrangementView, reservedRegions(kind:options:layoutDirectionBehavior:), onHingeChange(isEnabled:_:), toolbarVerticalBehavior(_:), toolbarVerticalEdge. ↩

  17. Antoine van der Lee, iPhone Duo Simulator: Testing and optimizing your SwiftUI app, SwiftLee, 2026년 9월 22일, 인용. Artem Novichkov, iPhone Duo by Examples, GitHub README, “Good to Know”, 2026년 10월 2일 확인: “Its frame is the same in both states: 40 pt wide, with 20 pt margins on each side of a zero-width fold line.”(프레임은 두 상태에서 같다), “Reserved regions arrive after the first layout pass.”(예약 영역은 첫 레이아웃 패스 이후에 도착한다), “When folded, the outer display has no reserved regions at all, not even inactive ones.”(접혔을 때 외부 디스플레이에는 비활성 영역조차 없이 예약 영역이 전혀 없다). 앞의 두 가지는 여기의 프로브와 일치하지만, 세 번째는 일치하지 않습니다. 프로브는 접은 외부 디스플레이에서 활성 occlusion 두 개를 읽었기 때문입니다. Mick MacCallum, How to Get Your App Ready for iPhone Duo, BleepingSwift, 2026년 9월 18일. Yurii Kleimenov, How to adapt your iOS app to iPhone Duo, Adapty, 2026년 9월 11일 게시, 9월 15일 업데이트 표시. ↩↩↩↩↩

  18. Apple, “App Store submissions now open for the latest OS releases”, developer news, 2026년 9월 9일: 2027년 4월부터 App Store Connect에 업로드되는 앱은 “need to meet the following minimum requirements”(다음 최소 요건을 충족해야 한다)이며, 그 첫 번째가 “iOS and iPadOS apps must be built with the iOS 27 & iPadOS 27 SDK or later”입니다. ↩

  19. Apple, Get ready for iPhone Duo, 2026년 10월 2일 확인: Tech Talk 여섯 편, Group Lab 녹화본 두 편, Group Lab 질의응답 링크, Photos and Camera, SwiftUI, UIKit 포럼 Q&A, Xcode 27.1 beta, 디자인 가이드와 리소스, 준비 가이드, 대면 워크숍. Group Lab Q&A 페이지와 포럼 스레드는 읽지 않았습니다. 요청에 사람 확인 페이지가 돌아왔기 때문입니다. ↩↩↩

  20. Apple, Xcode 27.1 Beta Release Notes, 2026년 10월 2일 문서 JSON 엔드포인트로 확인, Simulator, Known Issues: “StandBy is unavailable in the iPhone Duo Simulator runtime. (187708663)” 및 “Running and debugging most app extensions is unavailable in the iPhone Duo Simulator runtime. (187708767)”. Apple, Xcode 27.2 Beta 2 Release Notes, 2026년 10월 2일 같은 방식으로 확인: Overview, “Download Xcode 27.1 beta to get the iOS SDK and simulator support for iPhone Duo.”(iPhone Duo용 iOS SDK와 시뮬레이터 지원을 받으려면 Xcode 27.1 beta를 내려받으라); General, Known Issues, 187146039, 인용. ↩

관련 게시물

Xcode 27.1 beta: iPhone Duo 시뮬레이터 속의 내 앱

Xcode 27.1 beta는 첫 iOS 27.1 SDK와 iPhone Duo 시뮬레이터를 함께 싣고 왔습니다. SDK에 무엇이 들어갔는지, 디바이스 프로파일이 두 디스플레이를 어떻게 적어 두었는지, 프로브가 포즈마…

33 분 소요

arm64e.x1: Apple의 검사된 포인터 연산 슬라이스

iOS 27 RC 릴리스 노트에 arm64e.x1이 추가됐습니다. A20 Pro, M6, S11에서 동작하는 검사된 포인터 연산(CPA2), clang이 생성하는 명령, Xcode의 네 가지 스위치, 대응 하드웨어 …

15 분 소요

iPhone Duo를 위한 디자인: 움직이는 것, 나뉘는 것, 그대로 있는 것

Apple의 iPhone Duo 디자인 가이드와 세 편의 Tech Talk를 규칙으로 읽습니다. 포즈별 레이아웃이 아닌 두 개의 사이즈 클래스, 접힘부에서의 변위, arrangement, 그리고 사이드 바.

17 분 소요