iOS

Xcode 27.1 Mac Catalyst 컴파일 오류: iOS 27.1 API가 없는 이유와 최소 수정

뉴랜드 2026. 10. 17. 12:34
반응형

Xcode 27.1로 올린 뒤 Mac Catalyst 빌드가 has no member, cannot find 계열 오류로 멈추고 실행 대상 목록에서 My Mac이 사라졌다면 코드를 의심하기 전에 SDK 조합을 확인해야 합니다. iOS 27.1 전용 API는 Mac Catalyst용 UIKit에 아예 들어 있지 않습니다. Xcode 27.1에서 iOS SDK만 27.1로 올라가고 macOS SDK는 27.0으로 남아, 카탈리스트가 참조하는 UIKit이 iOS 27.0 수준에 머물렀습니다. 애플도 이 두 가지를 릴리스 노트에 알려진 문제(185924957, 187046347)로 등록했습니다.

증상: 어떤 오류 문구로 멈추나요

오류 문구는 문제가 된 API 이름과 함께 나옵니다. Xcode 27.1(27A9275), iPhoneOS 27.1 SDK, macOSX 27.0 SDK가 설치된 환경에서 iOS 27.1 전용 API 네 개를 Mac Catalyst 타깃으로 컴파일해 실제로 받은 문구는 아래와 같습니다.

실측 오류 문구 호출한 API
value of type 'UIView' has no member 'reservedRegions' UIView.reservedRegions(kind:options:)
value of type 'UIBarButtonItem' has no member 'axisBehavior' UIBarButtonItem.axisBehavior
type 'UISceneAccessory' has no member 'cameraCapture' UISceneAccessory.cameraCapture(sceneConfiguration:)
cannot find 'ToolbarVerticalCompressionBehavior' in scope ToolbarVerticalCompressionBehavior.prefersTabBar

문구만 보면 API 이름을 잘못 쓴 것처럼 보입니다. 실제로는 심볼이 없어서 나오는 오류입니다. 같은 코드가 iOS 27.1 타깃에서는 오류 없이 통과합니다.

실측: 같은 소스가 iOS에서는 통과하고 카탈리스트에서만 실패합니다

확인에 쓴 소스는 아래가 전부입니다.

// probe.swift
import UIKit

func probe(_ view: UIView, _ item: UIBarButtonItem) {
    _ = view.reservedRegions(kind: .occlusion)
    item.axisBehavior = .horizontalOnly
}
# iOS 27.1 타깃: 오류 없음
xcrun swiftc -typecheck -target arm64-apple-ios27.1 \
  -sdk "$(xcrun --sdk iphoneos --show-sdk-path)" probe.swift

# Mac Catalyst 타깃: 심볼 부재 오류
CAT_SDK="$(xcrun --sdk macosx --show-sdk-path)"
xcrun swiftc -typecheck -target arm64-apple-ios27.1-macabi \
  -sdk "$CAT_SDK" \
  -F "$CAT_SDK/System/iOSSupport/System/Library/Frameworks" probe.swift
# error: value of type 'UIView' has no member 'reservedRegions'
# error: value of type 'UIBarButtonItem' has no member 'axisBehavior'

두 번의 컴파일에서 소스는 같습니다. 달라진 것은 타깃과 SDK뿐입니다. 카탈리스트는 별도 SDK가 아니라 macOS SDK 안의 iOSSupport 프레임워크를 쓰기 때문에, 컴파일 명령에도 -F로 그 경로를 직접 넣었습니다.

원인 1: Mac Catalyst용 UIKit은 macOS SDK 안에 있습니다

경로부터 다릅니다. 카탈리스트용 UIKit은 iPhoneOS SDK가 아니라 macOS SDK 아래에 있습니다.

CAT_SDK="$(xcrun --sdk macosx --show-sdk-path)/System/iOSSupport/System/Library/Frameworks/UIKit.framework/Headers"
for h in UIViewReservedRegion UIHinge UIArrangementViewController; do
  test -f "$CAT_SDK/$h.h" && echo "$h.h 있음" || echo "$h.h 없음"
done
# UIViewReservedRegion.h 없음
# UIHinge.h 없음
# UIArrangementViewController.h 없음

iOS 27.1 전용 선언이 들어 있는 헤더는 iOS SDK 쪽에 16개, 선언은 48줄입니다. 같은 시기 macOS SDK의 iOSSupport 쪽에는 iOS 27.1 선언이 한 줄도 없습니다.

IOS_SDK="$(xcrun --sdk iphoneos --show-sdk-path)"
CAT_SDK="$(xcrun --sdk macosx --show-sdk-path)/System/iOSSupport"

grep -ro "ios(27.1)" "$IOS_SDK/System/Library/Frameworks/"*/Headers/ | wc -l        # 48
grep -ro "ios(27.1)" "$IOS_SDK/System/Library/Frameworks/"*/Headers/ \
  | cut -d: -f1 | sort -u | wc -l                                                   # 16
grep -ro "ios(27.1)" "$CAT_SDK/System/Library/Frameworks/"*/Headers/ | wc -l        # 0

카탈리스트 빌드가 iOS 27.0 수준의 UIKit을 보고 있다는 뜻입니다. 헤더가 통째로 빠진 API도 있고(UIViewReservedRegion.h, UIHinge.h), 헤더는 있지만 iOS 27.1 메서드만 빠진 API도 있습니다(UISceneAccessory.h의 cameraCapture).

원인 2: 이번에는 iOS SDK만 27.1로 올라갔습니다

App Store Connect 릴리스 노트가 2026년 10월 15일 자로 알린 내용이 이 비대칭을 그대로 보여줍니다.

You can now upload apps built with Xcode 27.1 using the SDK for iOS 27.1, 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.1이고 iPadOS, macOS, tvOS, visionOS, watchOS는 27.0입니다. Xcode 27.1에 들어 있는 SDK 목록도 같습니다.

xcodebuild -showsdks | grep -E "iOS 27\.1|macOS 27\.0"
# iOS 27.1            -sdk iphoneos27.1
# Simulator - iOS 27.1  -sdk iphonesimulator27.1
# macOS 27.0          -sdk macosx27.0

Xcode 27.1은 2026년 10월 15일 태평양시각에 정식 공개됐습니다. 애플 릴리스 목록에는 iOS 27.1 정식이 아직 없고 27.2 베타만 올라와 있습니다. 10월 23일 아이폰 듀오 출시에 맞춰 iOS SDK가 먼저 올라갔습니다. 카탈리스트가 참조하는 macOS SDK는 27.0에 남았습니다.

내 프로젝트에서 걸리는 API를 먼저 찾는 방법

카탈리스트 빌드를 돌려 오류를 하나씩 고쳐도 됩니다. 위험한 API를 미리 알면 수정 범위를 잡기 쉽습니다. 아래 명령이 iOS 27.1 전용 선언이 있는 헤더 16개를 그대로 뽑아 줍니다.

grep -rl "ios(27.1)" "$IOS_SDK/System/Library/Frameworks/"*/Headers/ | sed "s|.*/Frameworks/||"

UIKit 쪽은 UIBarButtonItem, UIView, UISceneAccessory, UIVerticalBarEdge, UIViewController, UIArrangementViewController, UINavigationItem, UIViewLayoutRegion, UIHinge, UIWindowScene, UIHingeInteraction, UISplitArrangement, UIViewReservedRegion, UIOverlayArrangement입니다. 나머지는 AVFoundation의 AVCaptureDevice와 AVKit의 AVCaptureDeviceDirectionCoordinator입니다.

이름을 보면 성격이 드러납니다. 힌지 상태, 화면 분할 배치, 다른 요소가 차지한 예약 영역, 보조 디스플레이용 카메라 캡처 액세서리, 세로 막대 압축 동작처럼 접이식과 다중 화면 기기를 다루는 API입니다. Swift 인터페이스에도 같은 선언이 있어 SwiftUI의 CameraCaptureAccessory와 ToolbarVerticalCompressionBehavior 역시 카탈리스트에서는 찾을 수 없는 심볼입니다.

최소 수정 1: 조건부 컴파일로 코드를 갈라 놓습니다

심볼 자체가 없으므로 런타임 분기로는 해결되지 않습니다. 컴파일 시점에 갈라 놓아야 합니다.

import UIKit

func applyDuoMargins(to view: UIView) {
#if targetEnvironment(macCatalyst)
    // 카탈리스트용 UIKit에는 iOS 27.1 예약 영역 API가 없습니다.
    applyFallbackMargins(to: view)
#else
    guard #available(iOS 27.1, *) else {
        applyFallbackMargins(to: view)
        return
    }
    let regions = view.reservedRegions(kind: .occlusion)
    for region in regions where region.isActive {
        applyMargins(region.margins, to: view)
    }
#endif
}

Objective-C 파일에서는 TARGET_OS_MACCATALYST를 씁니다.

#if !TARGET_OS_MACCATALYST
    if (@available(iOS 27.1, *)) {
        [self applyReservedRegionMargins];
    }
#endif

수정 지점이 여러 파일에 흩어져 있다면 iOS 27.1 API를 쓰는 코드만 따로 파일로 모으고 대상의 Target Membership에서 Mac Catalyst를 빼는 편이 낫습니다. 조건부 컴파일이 쌓이면 iOS 코드의 흐름이 읽기 어려워집니다.

최소 수정 2: 실행 대상이 사라졌다면 배포 버전을 확인합니다

배포 타깃을 iOS 27.1로 올린 대상은 Mac Catalyst 실행 대상이 목록에서 빠집니다. 카탈리스트가 허용하는 배포 버전 목록이 27.0에서 끝나기 때문입니다.

python3 - <<'PYEOF'
import plistlib
p = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/SDKSettings.plist"
iosmac = plistlib.load(open(p, "rb"))["SupportedTargets"]["iosmac"]
print(iosmac["DeploymentTargetSettingName"], iosmac["MaximumDeploymentTarget"])
print(iosmac["ValidDeploymentTargets"][-4:])
PYEOF
# IPHONEOS_DEPLOYMENT_TARGET 27.0.99
# ['26.5', '26.6', '26.6.1', '27.0']

카탈리스트의 배포 버전은 IPHONEOS_DEPLOYMENT_TARGET으로 관리합니다. 허용 목록의 마지막 값이 27.0입니다. iOS 27.1을 그대로 물려받은 카탈리스트 빌드는 유효한 배포 버전을 찾지 못해 실행 대상에서 빠집니다. 애플이 안내한 우회는 대상 설정에서 Mac Catalyst 최소 배포를 27.0으로 두는 것입니다. iOS 쪽 27.1은 그대로 두고 카탈리스트 값만 27.0으로 낮추는 형태입니다. Xcode의 최소 배포 버전 항목에서 플랫폼별 값을 따로 지정하면 됩니다.

적용 뒤에는 실행 대상이 돌아왔는지 확인합니다.

xcodebuild -project YourApp.xcodeproj -scheme YourApp -showdestinations

배포 버전을 내려도 컴파일 오류는 남습니다. 실행 대상 문제와 컴파일 오류는 원인이 다르므로 각각 처리해야 합니다.

흔히 시도하지만 효과가 없는 조치

  • if #available(iOS 27.1, *)만 추가하기: 심볼 자체가 없어 같은 오류가 반복됩니다. 같은 소스에 이 분기만 넣고 카탈리스트로 컴파일해 확인했습니다.
  • Clean Build Folder, 파생 데이터 삭제, Xcode 재설치: SDK에 없는 심볼은 다시 설치해도 생기지 않습니다.
  • cannot find를 링크 오류로 보고 라이브러리 검색 경로 고치기: 컴파일 단계에서 나오는 심볼 부재이므로 링크 설정과 무관합니다.
  • Swift Package 의존성에서 같은 오류가 날 때 그 패키지 버전을 내리는 것: 패키지가 iOS 27.1 API를 쓴다는 신호일 뿐입니다. 카탈리스트 대상에서 제외하거나 조건부 컴파일이 들어간 버전을 기다리는 편이 정확합니다.

아이폰 듀오 대응과 겹치는 지점

이번 SDK 비대칭은 아이폰 듀오 대응과 맞물립니다. 10월 23일 출시를 앞두고 듀오용 API를 앱에 넣기 시작하면, 가드 없이 넣은 코드가 카탈리스트 빌드를 먼저 막습니다. 듀오 대응의 화면 점검 순서는 앞서 정리한 글에 있습니다. 시뮬레이터 익스텐션 제약은 별도 글로 다뤘습니다.

정식 릴리스 노트에도 시뮬레이터 제약이 그대로 남아 있습니다. 아이폰 듀오 시뮬레이터 런타임에서는 StandBy를 쓸 수 없고 대부분의 앱 익스텐션 실행과 디버깅도 지원되지 않습니다. 시뮬레이터 첫 실행이 몇 분 걸릴 수 있다는 항목도 함께 올라 있습니다.

확인과 되돌리기

  • 카탈리스트와 iOS 양쪽에서 컴파일 오류가 0인지 확인합니다. 가드가 iOS 코드까지 막으면 듀오 대응 코드가 조용히 빠집니다.
  • -showdestinations로 My Mac이 실행 대상 목록에 돌아왔는지 확인합니다.
  • 카탈리스트를 당장 빌드할 필요가 없다면 Xcode 27.0을 유지하는 선택도 있습니다. 다만 듀오 대응을 시작하면 27.1 SDK가 필요합니다.
  • 애플이 27.1 이후 빌드에서 이 항목을 해소했는지 릴리스 노트로 확인합니다. 해소되면 조건부 컴파일을 걷어낼 수 있습니다.

참고 자료

반응형