ADR-0011: Expo SDK 56 → 57 로 업그레이드해 Xcode 27(Swift 6.4) 빌드를 지원한다
Context
macOS 27 + Xcode 27(Swift 6.4)로 개발 환경을 올린 뒤, Expo SDK 56 네이티브 코드가 연쇄적으로 컴파일 실패했다. Xcode 27은 이전 버전이 모듈 시스템으로 덮어주던 것들을 더 이상 봐주지 않는다. 발견된 3겹:
- Pod deployment target < 15.0 — KSCrash·SDWebImage·privacy manifest 번들 등이 podspec 에 9~13.4 를 선언. Xcode 27은 15.0 미만 타깃 빌드를 거부.
expo-modules-jsiSwift 삼항식 C 함수 포인터 —JavaScriptRuntime.swift가set == nil ? nil : setter로 C 함수 포인터를 형성. Swift 6.4는 “C 함수 포인터는 func 참조/리터럴 클로저에서만” 으로 엄격화(Expo #46876).@datadog/mobile-react-native생성-Swift.h가RCTPromiseResolveBlock/RejectBlock을 참조하는데, 정적 프레임워크 module context 에서 그 typedef 를 import 하지 못해 “unknown type name” (DataDog #1382, 업스트림 미해결).
제약: 이 레포는 iOS 스토어 빌드를 릴리즈 담당자 로컬 fastlane 으로 낸다(.xcode-version 핀 없음, CI iOS 빌드 없음). 즉 Xcode 27 비호환은 로컬 개발뿐 아니라 스토어 릴리즈도 막는다.
그때 몰랐던 것: SDK 57 이 #1·#2 를 네이티브로 이미 해결한다는 것(RN 0.86 이 라이브러리 pod 타깃을 15.1 로 올리고, expo-modules-autolinking #47562 가 리소스 번들 타깃을 정렬, jsi 는 setter 포인터를 명시 타입으로 분리). #3(Datadog)만 SDK 와 무관하게 남는다.
검토한 대안
| 대안 | 채택 안 한 이유 |
|---|---|
| A. Xcode 26.4 고정(SDK 56 유지) | 코드 변경 0 으로 당장은 깨끗하나, 릴리즈 담당자 로컬 Xcode 까지 26.4 로 고정을 강제해야 지속된다(핀 인프라 없음). Expo/RN 이 계속 앞서가므로 결국 업그레이드는 불가피 — 시점만 미룸. |
| B. SDK 56 유지 + Xcode 27 shim | deployment-target config plugin(pod 타깃 15.1 상향) + expo-modules-jsi yarn resolution + Datadog 패치를 직접 떠안는다. #1·#2 는 SDK 57 이 공짜로 주는 것을 손으로 유지하는 셈이고, SDK 57 로 올리는 순간 전부 버려질 임시물. |
Decision
Expo SDK 56 → 57 로 업그레이드한다. react-native 0.85.3 → 0.86.3(react/react-dom 은 19.2.3 유지 — RN renderer 고정 제약 불변), expo 모듈 ~30개 57.x, reanimated 4.5.1·worklets 0.10.1·gesture-handler 2.32.0. expo install --fix 가 되돌린 의도적 오버라이드(@sentry/react-native ^8.12.0, keyboard-controller ^1.21.12)는 복구한다.
#3(Datadog)은 SDK 버전과 무관하므로 patch-package 로 처리한다: 코어 5개(DdLogs/DdRum/DdTrace/DdFlags/DdSdk)+ session-replay 1개 *Implementation.swift 의 RCTPromiseResolveBlock/RejectBlock 파라미터를 구조적 클로저(@escaping ((Any?) -> Void) / @escaping ((String?, String?, NSError?) -> Void))로 치환 — Datadog 자체가 setEvaluationContext 에 이미 쓰는 검증된 패턴. 배선: patches/@datadog+mobile-react-native+3.6.0.patch·patches/@datadog+mobile-react-native-session-replay+3.6.0.patch.
SDK 56 shim(deployment-target 플러그인·jsi resolution)은 포함하지 않는다(SDK 57 이 네이티브로 해결).
Consequences
좋아지는 것
- Xcode 27(Swift 6.4)에서 네이티브 빌드가 통과 — 로컬 개발과 로컬 fastlane 스토어 빌드 모두.
- #1·#2 를 위한 커스텀 shim(플러그인·resolution)이 불필요 — 유지보수 표면 감소.
- 최신 Expo/RN 툴체인에 정렬(이후 SDK 캐치업 부담 완화).
감수하는 것
- Datadog 패치를 수동 유지한다(업스트림 #1382 미해결, PR #1408 대기). Datadog SDK bump 마다 patch 재적용/재생성 필요.
- RN 0.85→0.86 마이너 업그레이드 회귀 리스크 — iOS(Xcode 27)·Android 빌드 + 실행 + 스타일(nativewind/react-native-css) 런타임을 검증했으나, 전면 회귀는 스토어 릴리즈 전 QA 로 커버.
- 유저 반영은 OTA 불가 — 네이티브 변경이라 다음 native-release(스토어 재빌드)로만 도달.
이 결정을 다시 열어야 할 때
- DataDog #1408 공식 수정이 릴리즈되면 → Datadog 버전 bump 하고
patches/@datadog+*를 제거한다. - 다음 Xcode/Expo SDK bump 시 이 문서의 3겹 비호환 목록을 재점검한다(같은 함정이 다른 Swift pod 에도 재발 가능).