@mcamdali/sdkbump 0.0.0-stage → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.de.md +251 -0
- package/README.es.md +252 -0
- package/README.fr.md +252 -0
- package/README.ja.md +250 -0
- package/README.ko.md +250 -0
- package/README.md +322 -2
- package/README.pt-BR.md +252 -0
- package/README.ru.md +252 -0
- package/README.tr.md +338 -0
- package/README.zh-CN.md +250 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +11 -0
- package/dist/cli/run.d.ts +29 -0
- package/dist/cli/run.js +111 -0
- package/dist/collect/app.d.ts +48 -0
- package/dist/collect/app.js +117 -0
- package/dist/collect/blank.d.ts +6 -0
- package/dist/collect/blank.js +75 -0
- package/dist/collect/pbxproj.d.ts +45 -0
- package/dist/collect/pbxproj.js +260 -0
- package/dist/collect/plist-binary.d.ts +2 -0
- package/dist/collect/plist-binary.js +123 -0
- package/dist/collect/plist-value.d.ts +15 -0
- package/dist/collect/plist-value.js +14 -0
- package/dist/collect/plist-xml.d.ts +2 -0
- package/dist/collect/plist-xml.js +195 -0
- package/dist/collect/plist.d.ts +6 -0
- package/dist/collect/plist.js +19 -0
- package/dist/collect/regions.d.ts +9 -0
- package/dist/collect/regions.js +108 -0
- package/dist/collect/sources.d.ts +45 -0
- package/dist/collect/sources.js +99 -0
- package/dist/core/files.d.ts +22 -0
- package/dist/core/files.js +47 -0
- package/dist/core/lines.d.ts +2 -0
- package/dist/core/lines.js +21 -0
- package/dist/core/versions.d.ts +18 -0
- package/dist/core/versions.js +63 -0
- package/dist/detect/project.d.ts +46 -0
- package/dist/detect/project.js +220 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +27 -0
- package/dist/model.d.ts +93 -0
- package/dist/model.js +2 -0
- package/dist/report/index.d.ts +12 -0
- package/dist/report/index.js +22 -0
- package/dist/report/markdown.d.ts +2 -0
- package/dist/report/markdown.js +113 -0
- package/dist/report/text.d.ts +27 -0
- package/dist/report/text.js +63 -0
- package/dist/report/tty.d.ts +7 -0
- package/dist/report/tty.js +112 -0
- package/dist/rules/check.d.ts +11 -0
- package/dist/rules/check.js +253 -0
- package/dist/rules/data.d.ts +217 -0
- package/dist/rules/data.js +141 -0
- package/dist/rules/fixes.d.ts +28 -0
- package/dist/rules/fixes.js +161 -0
- package/dist/rules/patterns.d.ts +29 -0
- package/dist/rules/patterns.js +109 -0
- package/dist/rules/query.d.ts +16 -0
- package/dist/rules/query.js +50 -0
- package/dist/scan.d.ts +10 -0
- package/dist/scan.js +115 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +7 -0
- package/package.json +69 -4
- package/rules/ios27.json +613 -0
- package/rules/schema.json +449 -0
package/README.ko.md
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
<!-- i18n-source: README.md@49073c524eb2b7aad433b6794961ec40869c1170 status=reviewed -->
|
|
2
|
+
<!-- brand:start -->
|
|
3
|
+
|
|
4
|
+
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/banner-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/banner-light.svg"><img src="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/banner-light.svg" alt="sdkbump: Checks native iOS, Flutter and React Native apps for the iOS 27 SDK changes and names the fix for each."></picture></p>
|
|
5
|
+
|
|
6
|
+
<!-- lang-selector:start -->
|
|
7
|
+
<p align="center"><a href="README.md">English</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.es.md">Español</a> · <a href="README.de.md">Deutsch</a> · <a href="README.fr.md">Français</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.ru.md">Русский</a></p>
|
|
8
|
+
<!-- lang-selector:end -->
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<a href="https://github.com/muhittincamdali/sdkbump/actions/workflows/ci.yml"><img src="https://github.com/muhittincamdali/sdkbump/actions/workflows/ci.yml/badge.svg" alt="CI 상태"></a>
|
|
12
|
+
<a href="https://github.com/muhittincamdali/sdkbump/releases"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-version-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-version-light.svg"><img src="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-version-light.svg" alt="최신 버전: v0.1.2"></picture></a>
|
|
13
|
+
<a href="https://github.com/muhittincamdali/sdkbump/blob/main/package.json"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-language-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-language-light.svg"><img src="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-language-light.svg" alt="언어 버전: Node.js 22.12+"></picture></a>
|
|
14
|
+
<a href="https://github.com/muhittincamdali/sdkbump/blob/main/LICENSE"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-license-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-license-light.svg"><img src="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-license-light.svg" alt="라이선스: MIT"></picture></a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<a href="#빠른-시작"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-start-ko-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-start-ko-light.svg"><img src="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-start-ko-light.svg" alt="시작하기"></picture></a>
|
|
19
|
+
<a href="https://muhittincamdali.github.io/sdkbump/"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-docs-ko-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-docs-ko-light.svg"><img src="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-docs-ko-light.svg" alt="문서"></picture></a>
|
|
20
|
+
<a href="https://github.com/muhittincamdali/sdkbump/tree/main/docs/guides"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-guides-ko-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-guides-ko-light.svg"><img src="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-guides-ko-light.svg" alt="가이드"></picture></a>
|
|
21
|
+
<a href="https://github.com/muhittincamdali/sdkbump/tree/main/examples"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-examples-ko-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-examples-ko-light.svg"><img src="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-examples-ko-light.svg" alt="예제"></picture></a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
> 이 문서는 영어 README의 번역본입니다. 내용이 다를 경우 영어 버전이 최신입니다.
|
|
25
|
+
|
|
26
|
+
<!-- brand:end -->
|
|
27
|
+
|
|
28
|
+
Xcode 27로 빌드한 앱을 출시하기 전에: sdkbump는 네이티브 iOS, Flutter, React Native/Expo 프로젝트에서 iOS 27 SDK가 바꾸는 부분(우선 UIScene 생명 주기)을 검사하고, 각 항목에 어떤 해결 방법이 맞는지 알려 줍니다. 프로젝트 파일을 읽기만 하며, 빌드하거나 실행하거나 변경하지 않습니다.
|
|
29
|
+
|
|
30
|
+
<picture><source media="(prefers-color-scheme: dark)" srcset="docs/media/demo-rn-087-dark.svg"><source media="(prefers-color-scheme: light)" srcset="docs/media/demo-rn-087-light.svg"><img src="docs/media/demo-rn-087-light.svg" alt="React Native 0.87 앱에서 실행한 sdkbump: iOS 27 SDK로 빌드하면 실행되지 않음. SCN001은 scene 구성 없음, SCN002는 app delegate에서 window 생성이며, 해결 방법은 세 가지"></picture>
|
|
31
|
+
|
|
32
|
+
<sub>실제 CLI에서 녹화했습니다. 대상은 <a href="fixtures/rn-087">rn-087 fixture</a>, 즉 <code>@react-native-community/cli init</code>으로 만든 React Native 0.87.1 앱입니다(<code>npm run demo</code>).</sub>
|
|
33
|
+
|
|
34
|
+
## 주요 기능
|
|
35
|
+
|
|
36
|
+
- **"실행될까?"에 답합니다.** scene을 선언하지 않은 앱은 Xcode 27로 빌드되지만 실행할 때 멈춥니다. sdkbump는 테스트 기기에서 직접 겪기 전에 이를 알려 주고, 바꿔야 할 파일을 알려 줍니다.
|
|
37
|
+
- **네이티브, Flutter, React Native, Expo**를 같은 규칙 ID로 하나의 보고서에 담습니다. 출력은 텍스트, Markdown, JSON 중에서 고를 수 있고, CI용 종료 코드도 제공합니다.
|
|
38
|
+
- **내 프로젝트에 맞는 해결 방법**: React Native 0.88 또는 직접 작성한 scene delegate, Expo SDK 57의 옵트인 또는 SDK 58, Flutter의 자동 마이그레이션 또는 수동 절차(sdkbump는 Flutter 마이그레이터와 같은 조건을 확인합니다), SwiftUI 앱이라면 SwiftUI API.
|
|
39
|
+
- **조용히 깨지는 부분도**: 앱에 scene이 생기면 UIKit이 더 이상 사용하지 않는 app delegate 메서드와 launch option(링크, Handoff, 퀵 액션, 포그라운드와 백그라운드 전환), 그리고 올바른 window를 더 이상 찾지 못하는 window 조회.
|
|
40
|
+
- **모든 설명에는 출처가 있습니다.** 규칙은 데이터로 관리되며([rules/ios27.json](rules/ios27.json)) 각 규칙은 Apple, Flutter, React Native, Expo가 직접 공개한 페이지를 인용합니다. 출처가 없는 규칙이 있으면 테스트가 실패합니다.
|
|
41
|
+
- **iOS 27 시뮬레이터에서 확인했습니다**: SCN001과 SCN002의 배경이 되는 실행 실패와 검은 화면을 Xcode 27.0으로 재현했으며, 녹화본은 저장소에 있습니다.
|
|
42
|
+
|
|
43
|
+
## 빠른 시작
|
|
44
|
+
|
|
45
|
+
[Node.js](https://nodejs.org/) 22.12 이상이 필요합니다(`node --version`으로 확인할 수 있고, Mac에서는 `brew install node`로 설치할 수 있습니다). 그 외에는 필요한 것이 없습니다.
|
|
46
|
+
|
|
47
|
+
**1. 앱 폴더에서 터미널을 엽니다.** `.xcodeproj`, `pubspec.yaml`, `package.json` 중 하나가 있는 폴더입니다.
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
cd path/to/YourApp
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**2. sdkbump를 실행합니다.**
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
npx @mcamdali/sdkbump
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
npm 패키지 이름은 `@mcamdali/sdkbump`이고, 설치되는 명령은 `sdkbump`입니다. scope 없이 `npx sdkbump`를 실행하면 이 패키지가 실행되지 않습니다.
|
|
60
|
+
|
|
61
|
+
**3. 프로젝트 이름 아래의 첫 번째 줄을 읽습니다.** "Fails to launch when built with the iOS 27 SDK"(iOS 27 SDK로 빌드하면 실행되지 않음)는 **ERROR** 항목을 고칠 때까지 앱이 실행 시점에 멈춘다는 뜻이며, 각 항목은 "How to fix" 목록의 어떤 해결 방법이 맞는지 알려 줍니다. **WARNING** 항목은 실행을 막지는 않지만, 앱에 scene이 생기면 동작하지 않게 됩니다. "Launch not determined"(실행 여부를 판단할 수 없음)는 sdkbump가 Info.plist를 읽지 못했다는 뜻이며, 어떤 파일을 왜 읽지 못했는지는 보고서 끝의 참고 사항에 나옵니다(아래 "한계" 참고).
|
|
62
|
+
|
|
63
|
+
## iOS 27 SDK로 빌드하면 무엇이 달라지나요
|
|
64
|
+
|
|
65
|
+
Apple의 [Transitioning to the UIKit scene-based life cycle](https://developer.apple.com/documentation/uikit/transitioning-to-the-uikit-scene-based-life-cycle)에서 인용합니다(번역).
|
|
66
|
+
|
|
67
|
+
> iOS 27, iPadOS 27, Mac Catalyst 27, tvOS 27, visionOS 27부터는 최신 SDK로 빌드한 앱이 scene 기반 생명 주기를 채택해야 하며, 그렇지 않으면 실행되지 않습니다.
|
|
68
|
+
|
|
69
|
+
1. **앱이 scene을 선언하지 않으면 실행할 때 멈춥니다.** Info.plist에 구성이 들어 있는 `UIApplicationSceneManifest`가 없고 `application(_:configurationForConnecting:options:)`도 없으면, UIKit은 "UIScene life cycle is required for apps built with this SDK"라는 메시지와 함께 프로세스를 종료합니다. 이 검사는 앱이 시작될 때 이루어지므로 컴파일러는 경고하지 않습니다. React Native 0.87 이하, 옵트인하지 않은 Expo SDK 57, Expo SDK 56 이하가 이런 앱을 만들며, Flutter 도구가 마이그레이션하지 못하는 Flutter 앱도 마찬가지입니다.
|
|
70
|
+
2. **scene이 있으면 app delegate는 더 이상 UI 이벤트를 받지 못합니다.** 링크, user activity, 퀵 액션, 활성/백그라운드 전환은 scene delegate로 전달되며, app delegate가 만든 window는 화면에 나타나지 않습니다.
|
|
71
|
+
|
|
72
|
+
[컴파일되는데도 충돌하는 이유](docs/guides/why-it-crashes.md)에서는 iOS 27.0 시뮬레이터에서 실행한 [실행 매트릭스](examples/launch-matrix/README.md)를 보여 줍니다.
|
|
73
|
+
|
|
74
|
+
## sdkbump가 검사하는 항목
|
|
75
|
+
|
|
76
|
+
| 규칙 | 심각도 | 항목 | iOS 27.0 시뮬레이터에서 확인한 결과 |
|
|
77
|
+
| ------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
|
|
78
|
+
| SCN001 | error | scene 구성이 없음: 구성된 `UIApplicationSceneManifest`도 `configurationForConnecting`도 없고 SwiftUI `App`도 아님. `ios/`가 없는 Expo는 SDK 버전과 `enableSceneSupport` 확인 | 실행할 때 멈춤 |
|
|
79
|
+
| SCN002 | error | app delegate가 `UIWindow(frame:)`으로 window를 생성함 | 앱에 scene이 생기면 검은 화면 |
|
|
80
|
+
| SCN003 | warning | scene이 있으면 UIKit이 더 이상 사용하지 않는 app delegate 메서드와 launch option 키(13개). 단, scene delegate가 이미 처리하는 경우는 제외 | SwiftUI 앱에서 `applicationDidEnterBackground(_:)`가 호출되지 않음 |
|
|
81
|
+
| SCN004 | warning | `UIApplication.shared.keyWindow`, `.windows` 또는 app delegate의 `window`를 통한 window 조회 | 컴파일러의 deprecation 경고만 표시됨 |
|
|
82
|
+
|
|
83
|
+
규칙 데이터는 `ios27@2026.10.15`이며 Xcode 27.0(27A266a)을 기준으로 확인했습니다. React Native 또는 Expo에 `react-native-uiscene-bridge`나 `react-native-xcode27`이 설치되어 있으면, 이들이 실행 시점에 처리하는 항목은 해당 README에 적힌 한계와 함께 "처리됨"으로 표시되며 실행 실패로 처리하지 않습니다.
|
|
84
|
+
|
|
85
|
+
## 설치
|
|
86
|
+
|
|
87
|
+
npm에서 `sdkbump` 명령을 설치합니다(패키지 `@mcamdali/sdkbump`).
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
npm i -g @mcamdali/sdkbump
|
|
91
|
+
sdkbump
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
프로젝트에서는 `npm install --save-dev @mcamdali/sdkbump`로 버전을 고정할 수 있고, 그다음 `npx sdkbump`가 그 사본을 실행합니다. 각 [GitHub 릴리스](https://github.com/muhittincamdali/sdkbump/releases)에는 같은 패키지가 `sdkbump-<버전>.tgz`로 `SHA256SUMS` 파일과 함께 들어 있으며, `npx`로 npm 없이 URL에서 바로 실행할 수 있습니다. `muhittincamdali/tap`용 Homebrew formula는 릴리스 워크플로가 준비합니다(tap의 첫 릴리스와 함께 제공할 계획입니다).
|
|
95
|
+
|
|
96
|
+
## 사용법
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
sdkbump [path] [options]
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
| 옵션 | 설명 |
|
|
103
|
+
| ----------------------- | ---------------------------------------------------------------------- |
|
|
104
|
+
| `path` | 프로젝트 폴더 또는 `.xcodeproj`. 기본값은 현재 폴더 |
|
|
105
|
+
| `-f, --format <format>` | `tty`(기본값), `markdown`, `json` |
|
|
106
|
+
| `-o, --output <file>` | 보고서를 터미널 대신 파일에 기록 |
|
|
107
|
+
| `--fail-on <severity>` | `error`(기본값), `warning`, `info`, `none`: 종료 코드가 1이 되는 기준 |
|
|
108
|
+
| `--no-color` | 터미널에서도 일반 텍스트로 출력(`NO_COLOR`와 `FORCE_COLOR`를 따릅니다) |
|
|
109
|
+
|
|
110
|
+
종료 코드: `0`은 `--fail-on` 이상에서 처리되지 않은 항목이 없음, `1`은 그 이상의 항목이 있음, `2`는 잘못된 인수, iOS 프로젝트를 찾지 못함 또는 예기치 않은 오류입니다.
|
|
111
|
+
|
|
112
|
+
sdkbump는 iOS 앱을 스스로 찾습니다. 네이티브 앱은 루트 또는 한 단계 아래에 있는 `.xcodeproj`, Flutter, React Native, Expo 앱은 `ios/`(Flutter 플러그인은 `example/ios/`)입니다. 프로젝트 파일에 있는 모든 iOS 앱 타깃, 각 타깃이 지정한 Info.plist(XML 또는 바이너리), 앱의 Swift 및 Objective-C 파일을 읽으며, Pods, `node_modules`, 빌드 결과물, 테스트는 건너뜁니다. `ios/`가 없는 Expo 앱은 package.json과 app.json으로 검사합니다.
|
|
113
|
+
|
|
114
|
+
### Markdown 보고서를 코드와 함께 보관하기
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
npx @mcamdali/sdkbump --format markdown --output sdkbump.md
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 스크립트에서 JSON 보고서 사용하기
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
npx @mcamdali/sdkbump --format json > sdkbump.json
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
각 항목에는 `rule`, `severity`, `file`, `line`, `message`, 단계와 가이드가 담긴 `fix`, `alternatives`, `mitigation`(우회용 패키지가 처리하면 설정됩니다), `sources`가 있습니다. 구조는 `schemaVersion` 0이며 1.0 이전에 바뀔 수 있습니다. [examples/shell/launch-blockers.sh](examples/shell/launch-blockers.sh)는 `jq`로 이를 걸러 냅니다.
|
|
127
|
+
|
|
128
|
+
### CI에서 실행하기
|
|
129
|
+
|
|
130
|
+
[examples/github-actions/sdkbump.yml](examples/github-actions/sdkbump.yml)은 iOS 앱을 건드리는 모든 pull request의 작업 요약에 보고서를 추가하고, error가 있으면 작업을 실패시킵니다.
|
|
131
|
+
|
|
132
|
+
### JavaScript에서 사용하기
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
import { render, scan } from 'sdkbump';
|
|
136
|
+
|
|
137
|
+
const report = await scan('path/to/app');
|
|
138
|
+
console.log(report.verdict.text);
|
|
139
|
+
console.log(render(report, 'markdown'));
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
실행해 볼 수 있는 예제는 [examples/api/verdicts.mjs](examples/api/verdicts.mjs)입니다.
|
|
143
|
+
|
|
144
|
+
## 데모
|
|
145
|
+
|
|
146
|
+
터미널 녹화본은 각 툴체인으로 만든 실제 프로젝트인 [fixtures](fixtures/README.md)에서 CLI를 실행해 얻은 것입니다.
|
|
147
|
+
|
|
148
|
+
| 프로젝트 | 녹화본 |
|
|
149
|
+
| --------------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
150
|
+
| React Native 0.87.1 앱 | 이 페이지 맨 위 |
|
|
151
|
+
| AppDelegate를 수정한 Flutter 앱(Flutter 3.24) | [라이트](docs/media/demo-flutter-light.svg) · [다크](docs/media/demo-flutter-dark.svg) |
|
|
152
|
+
| 어댑터 방식의 app delegate를 쓰는 SwiftUI 앱 | [라이트](docs/media/demo-swiftui-light.svg) · [다크](docs/media/demo-swiftui-dark.svg) |
|
|
153
|
+
|
|
154
|
+
iOS 27.0 시뮬레이터(iPhone 17, Xcode 27.0으로 빌드)에서 네이티브 UIKit fixture는 실행할 때 멈추고, 같은 앱을 scene delegate로 옮기면 실행됩니다.
|
|
155
|
+
|
|
156
|
+
<picture><source media="(prefers-color-scheme: dark)" srcset="docs/media/demo-simulator-native-uikit-dark.svg"><source media="(prefers-color-scheme: light)" srcset="docs/media/demo-simulator-native-uikit-light.svg"><img src="docs/media/demo-simulator-native-uikit-light.svg" alt="iOS 27.0 시뮬레이터에서 실행한 scripts/simulator-check.sh native-uikit: 실행 4초 후에도 동작하지 않으며, UIKit의 출력은 Application failed to launch: UIScene life cycle is required for apps built with this SDK"></picture>
|
|
157
|
+
|
|
158
|
+
<p align="center"><img src="docs/media/simulator/launched.png" width="480" alt="Xcode 27.0으로 빌드한 native-uikit-scenes와 native-swiftui fixture가 iOS 27.0이 설치된 iPhone 17 시뮬레이터에서 실행되는 모습"></p>
|
|
159
|
+
|
|
160
|
+
2026년 10월 15일에 Node.js 26.11이 설치된 Mac(Apple silicon, M5 Max)에서 다른 작업이 실행 중인 상태로 측정했습니다. fixture 10개에 대한 `node dist/cli/main.js` 실행 100번을 한 세트로 세 세트를 돌렸고, Node.js 시작 시간을 포함한 세트별 중앙값은 0.11초, 0.12초, 0.19초, 가장 느린 실행은 0.33초였습니다. 컴퓨터 부하가 클수록 시간이 늘어납니다.
|
|
161
|
+
|
|
162
|
+
## 상태
|
|
163
|
+
|
|
164
|
+
| 기능 | 상태 |
|
|
165
|
+
| ---------------------------------------------------------------------------------- | --------- |
|
|
166
|
+
| 네이티브, Flutter(앱과 플러그인 예제), React Native, Expo 프로젝트 감지 | 동작함 |
|
|
167
|
+
| 앱 자체의 Info.plist와 Swift/Objective-C 소스에 대한 SCN001부터 SCN004 | 동작함 |
|
|
168
|
+
| 생태계와 버전에 따른 해결 방법 선택, Flutter 자동 마이그레이션 확인, 우회용 패키지 | 동작함 |
|
|
169
|
+
| 터미널, Markdown, JSON 보고서, `--fail-on` 종료 코드 | 동작함 |
|
|
170
|
+
| 의존성: Pods, `node_modules`, Flutter 플러그인 소스(패키지 이름 포함) | 예정(0.2) |
|
|
171
|
+
| 우회 안내를 별도 규칙 SCN005로 제공, 15.0 미만 배포 대상에 대한 DPT001 | 예정(0.2) |
|
|
172
|
+
| `--verify`: CLI에서 iOS 27 시뮬레이터로 빌드하고 실행 | 예정(0.3) |
|
|
173
|
+
| 패턴 대신 Swift/Objective-C 구문 트리, 빌드된 `.app` 검사, SiriKit deprecation | 예정(0.5) |
|
|
174
|
+
| 네이티브 앱용 `fix --dry-run`, SARIF 출력, GitHub Action | 예정(0.8) |
|
|
175
|
+
| 안정된 규칙 스키마와 JSON 구조 | 예정(1.0) |
|
|
176
|
+
|
|
177
|
+
## 한계
|
|
178
|
+
|
|
179
|
+
- **컴파일러가 아니라 패턴입니다.** 규칙은 주석과 문자열을 공백으로 바꾼 Swift와 Objective-C 텍스트에 적용하는 정규 표현식입니다. 생성된 코드, 매크로 안의 코드, 흔하지 않은 방식으로 작성한 코드는 놓치거나 잘못 잡을 수 있습니다. 구문 트리는 예정(0.5)입니다.
|
|
180
|
+
- **앱 자체 소스만 읽습니다.** Pods, `node_modules`, Flutter 플러그인 등 의존성은 읽지 않으므로, UIKit이 더 이상 호출하지 않는 app delegate 메서드에 기대는 플러그인은 보고되지 않습니다. 의존성 검사는 예정(0.2)입니다.
|
|
181
|
+
- **빌드 설정은 project.pbxproj에서만 읽습니다.** sdkbump는 프로젝트 파일에서 `INFOPLIST_FILE`과 `GENERATE_INFOPLIST_FILE`을 읽고 `$(SRCROOT)`, `$(PROJECT_DIR)`, `$(TARGET_NAME)`, `$(PRODUCT_NAME)`을 펼칩니다. `.xcconfig` 파일은 읽지 않고, 다른 빌드 설정도 펼치지 않으며, 프로젝트 폴더 밖의 Info.plist도 읽지 않습니다. 타깃의 Info.plist를 읽지 못하면 추측하는 대신 SCN001을 info 항목으로 보고하고 판정을 "Launch not determined"로 표시합니다.
|
|
182
|
+
- **억제 파일이 없습니다.** 항목을 검토 완료로 표시할 수 없으며, 어떤 심각도에서 실행을 실패로 할지는 `--fail-on`으로 정합니다.
|
|
183
|
+
- **파일만 읽습니다.** sdkbump는 앱을 빌드하거나 실행하지 않습니다. `--verify`는 예정(0.3)입니다.
|
|
184
|
+
|
|
185
|
+
## 문서
|
|
186
|
+
|
|
187
|
+
API 레퍼런스와 가이드: [muhittincamdali.github.io/sdkbump](https://muhittincamdali.github.io/sdkbump/).
|
|
188
|
+
|
|
189
|
+
## 가이드
|
|
190
|
+
|
|
191
|
+
- [컴파일되는데도 충돌하는 이유](docs/guides/why-it-crashes.md)
|
|
192
|
+
- [네이티브 UIKit 및 SwiftUI 앱](docs/guides/native-ios.md)
|
|
193
|
+
- [Flutter: 수정한 AppDelegate와 플러그인](docs/guides/flutter.md)
|
|
194
|
+
- [0.88 이전의 React Native와 SDK 58 이전의 Expo](docs/guides/react-native-expo.md)
|
|
195
|
+
- [네이티브 모듈 작성자용: 콜드 스타트 시 링크와 알림](docs/guides/native-module-authors.md)
|
|
196
|
+
|
|
197
|
+
## 관련 도구
|
|
198
|
+
|
|
199
|
+
같은 변경을 다루는 다른 프로젝트도 있습니다. 어느 것이 맞는지는 필요한 것에 따라 다릅니다.
|
|
200
|
+
|
|
201
|
+
- [react-native-uiscene-bridge](https://github.com/Atomics-hub/react-native-uiscene-bridge)와 [react-native-xcode27](https://github.com/Atomics-hub/react-native-xcode27)은 코드를 바꾸지 않고도 실행 시점에 React Native 또는 Expo 앱이 실행되도록 합니다. 아직 업그레이드할 수 없을 때 적합하며, sdkbump는 이들을 인식합니다.
|
|
202
|
+
- [auditor-uiscene](https://github.com/Anzell/auditor-uiscene)은 셸 스크립트로 Flutter 앱과 플러그인을 점검합니다.
|
|
203
|
+
- Flutter의 자체 마이그레이터(3.41부터의 `flutter build ios`)는 수정하지 않은 Flutter 앱을 마이그레이션합니다.
|
|
204
|
+
- [flutter_spm_doctor](https://pub.dev/packages/flutter_spm_doctor)는 Flutter 플러그인을 검사합니다.
|
|
205
|
+
|
|
206
|
+
## 요구 사항
|
|
207
|
+
|
|
208
|
+
| 툴체인 | 최소 버전 |
|
|
209
|
+
| ------- | --------- |
|
|
210
|
+
| Node.js | 22.12 |
|
|
211
|
+
|
|
212
|
+
sdkbump에는 Xcode가 필요 없습니다. 파일을 읽기만 하므로 Linux에서도 실행됩니다. CI는 Node.js 22와 24(Linux)에서 테스트를 실행하며, 로컬에서는 280개가 모두 Mac(Apple silicon)의 Node.js 22.23, 24.22, 26.11에서 통과합니다. 시뮬레이터 녹화에는 Xcode 27이 설치된 Mac이 필요하며 CI에는 포함되지 않습니다.
|
|
213
|
+
|
|
214
|
+
## 아키텍처
|
|
215
|
+
|
|
216
|
+
```text
|
|
217
|
+
path ─► detectProject native | flutter | react-native | expo; iOS folder; versions
|
|
218
|
+
─► collectApp iOS app targets from project.pbxproj, their Info.plist (XML or binary),
|
|
219
|
+
Swift and Objective-C sources with comments and strings blanked
|
|
220
|
+
─► checkApp SCN001…SCN004, fix per ecosystem and version, workaround packages
|
|
221
|
+
rules/ios27.json what each rule says, fixes, sources (validated with zod)
|
|
222
|
+
─► render tty | markdown | json
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
- 감지 패턴은 주석과 문자열을 비워 둔 소스 텍스트에 대한 정규식이며, 필요한 곳에서는 app delegate나 scene delegate 안에 있는 것만 셉니다.
|
|
226
|
+
- 규칙 데이터는 코드와 분리되어 있고 버전이 매겨져 있습니다(`ios27@2026.10.15`). 규칙, 해결 방법, 우회 방법에 출처가 없으면 테스트가 실패합니다.
|
|
227
|
+
- CLI(`src/cli/`)는 인수를 해석하고, 색과 너비를 고르고, 종료 코드를 정하는 일만 합니다.
|
|
228
|
+
|
|
229
|
+
## 로드맵
|
|
230
|
+
|
|
231
|
+
[상태](#상태) 표를 참고하세요. 0.2에서는 의존성과 배포 대상, 0.3에서는 CLI에서 시뮬레이터 실행, 0.5에서는 구문 트리와 빌드된 앱 검사, 0.8에서는 자동 수정과 SARIF, 1.0에서는 안정된 스키마를 추가합니다. 각 릴리스의 변경 내용은 [CHANGELOG.md](CHANGELOG.md)에 있습니다.
|
|
232
|
+
|
|
233
|
+
## FAQ
|
|
234
|
+
|
|
235
|
+
**제 앱은 지금 iOS 27에서 잘 실행됩니다. 영향을 받나요?** Xcode 26 이하로 빌드했다면 이 검사는 해당 빌드에 적용되지 않습니다. 저희가 실행해 본 결과, iOS 26 SDK에 링크한 같은 코드는 iOS 27 시뮬레이터에서 실행되었습니다. Xcode 27로 하는 다음 빌드부터 적용됩니다.
|
|
236
|
+
|
|
237
|
+
**sdkbump로 실제 기기 테스트를 대신할 수 있나요?** 아니요. 파일을 읽기만 합니다. 이 저장소의 시뮬레이터 녹화는 iOS 27.0 시뮬레이터에서 얻은 것이며, 여기서는 어떤 것도 실제 기기에서 실행하지 않았습니다.
|
|
238
|
+
|
|
239
|
+
**왜 어떤 항목은 "처리됨"으로 표시되나요?** package.json에 있는 우회용 패키지(react-native-uiscene-bridge 또는 react-native-xcode27)가 실행 시점에 scene을 선언하기 때문입니다. 앱은 실행되지만 앱 자체에는 아직 scene 지원이 없습니다. 보고서에는 해당 패키지의 한계와 실제 해결 방법이 나옵니다.
|
|
240
|
+
|
|
241
|
+
**sdkbump가 제 프로젝트를 변경하나요?** 아니요. 프로젝트를 읽기만 하고, 요청한 보고서만 기록합니다.
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
## 기여하기
|
|
245
|
+
|
|
246
|
+
기여를 환영합니다. pull request를 열기 전에 [기여 가이드](CONTRIBUTING.md)와 [행동 강령](https://github.com/muhittincamdali/.github/blob/main/CODE_OF_CONDUCT.md)을 읽어 주세요. 보안 문제는 [보안 정책](SECURITY.md)에 따라 비공개로 제보해 주세요.
|
|
247
|
+
|
|
248
|
+
## 라이선스
|
|
249
|
+
|
|
250
|
+
MIT 라이선스로 배포됩니다. 자세한 내용은 [LICENSE](LICENSE)를 참고하세요.
|
package/README.md
CHANGED
|
@@ -1,3 +1,323 @@
|
|
|
1
|
-
|
|
1
|
+
<!-- brand:start -->
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/banner-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/banner-light.svg"><img src="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/banner-light.svg" alt="sdkbump: Checks native iOS, Flutter and React Native apps for the iOS 27 SDK changes and names the fix for each."></picture></p>
|
|
4
|
+
|
|
5
|
+
<!-- lang-selector:start -->
|
|
6
|
+
<p align="center"><a href="README.md">English</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.es.md">Español</a> · <a href="README.de.md">Deutsch</a> · <a href="README.fr.md">Français</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.ru.md">Русский</a></p>
|
|
7
|
+
<!-- lang-selector:end -->
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://github.com/muhittincamdali/sdkbump/actions/workflows/ci.yml"><img src="https://github.com/muhittincamdali/sdkbump/actions/workflows/ci.yml/badge.svg" alt="CI status"></a>
|
|
11
|
+
<a href="https://github.com/muhittincamdali/sdkbump/releases"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-version-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-version-light.svg"><img src="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-version-light.svg" alt="Latest version: v0.1.2"></picture></a>
|
|
12
|
+
<a href="https://github.com/muhittincamdali/sdkbump/blob/main/package.json"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-language-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-language-light.svg"><img src="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-language-light.svg" alt="Language version: Node.js 22.12+"></picture></a>
|
|
13
|
+
<a href="https://github.com/muhittincamdali/sdkbump/blob/main/LICENSE"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-license-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-license-light.svg"><img src="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/badge-license-light.svg" alt="License: MIT"></picture></a>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<a href="#quick-start"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-start-en-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-start-en-light.svg"><img src="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-start-en-light.svg" alt="Get started"></picture></a>
|
|
18
|
+
<a href="https://muhittincamdali.github.io/sdkbump/"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-docs-en-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-docs-en-light.svg"><img src="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-docs-en-light.svg" alt="Documentation"></picture></a>
|
|
19
|
+
<a href="https://github.com/muhittincamdali/sdkbump/tree/main/docs/guides"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-guides-en-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-guides-en-light.svg"><img src="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-guides-en-light.svg" alt="Guides"></picture></a>
|
|
20
|
+
<a href="https://github.com/muhittincamdali/sdkbump/tree/main/examples"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-examples-en-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-examples-en-light.svg"><img src="https://raw.githubusercontent.com/muhittincamdali/sdkbump/main/.github/brand/button-examples-en-light.svg" alt="Examples"></picture></a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<!-- brand:end -->
|
|
24
|
+
|
|
25
|
+
Before you ship a build made with Xcode 27: sdkbump checks a native iOS, Flutter or React
|
|
26
|
+
Native/Expo project for what the iOS 27 SDK changes, starting with the UIScene life cycle, and
|
|
27
|
+
tells you which fix applies to each finding. It reads your project's files; it does not build,
|
|
28
|
+
run or change anything.
|
|
29
|
+
|
|
30
|
+
<picture><source media="(prefers-color-scheme: dark)" srcset="docs/media/demo-rn-087-dark.svg"><source media="(prefers-color-scheme: light)" srcset="docs/media/demo-rn-087-light.svg"><img src="docs/media/demo-rn-087-light.svg" alt="sdkbump run on a React Native 0.87 app: fails to launch when built with the iOS 27 SDK; SCN001 no scene configuration and SCN002 window created in the app delegate, with three ways to fix it"></picture>
|
|
31
|
+
|
|
32
|
+
<sub>Recorded from the real CLI on the <a href="fixtures/rn-087">rn-087 fixture</a>, a React
|
|
33
|
+
Native 0.87.1 app from <code>@react-native-community/cli init</code> (<code>npm run demo</code>).</sub>
|
|
34
|
+
|
|
35
|
+
## Highlights
|
|
36
|
+
|
|
37
|
+
- **Answers "will it launch?"** An app that declares no scene builds with Xcode 27 and then stops
|
|
38
|
+
at launch. sdkbump says so before you find out on a test device, and names the files to change.
|
|
39
|
+
- **Native, Flutter, React Native and Expo** in one report with the same rule IDs, as text,
|
|
40
|
+
Markdown or JSON, and an exit code for CI.
|
|
41
|
+
- **The fix that applies to your project**: React Native 0.88 or a hand-written scene delegate,
|
|
42
|
+
Expo SDK 57's opt-in or SDK 58, Flutter's automatic migration or the manual steps (sdkbump
|
|
43
|
+
checks the same conditions as Flutter's migrator), or the SwiftUI API for a SwiftUI app.
|
|
44
|
+
- **Quiet breakage too**: app delegate methods and launch options UIKit stops using once the app
|
|
45
|
+
has a scene (links, Handoff, quick actions, foreground and background), and window lookups that
|
|
46
|
+
no longer find the right window.
|
|
47
|
+
- **Every statement has a source.** The rules are data ([rules/ios27.json](rules/ios27.json)) and
|
|
48
|
+
each one cites Apple's, Flutter's, React Native's or Expo's own pages; the tests fail if one
|
|
49
|
+
does not.
|
|
50
|
+
- **Checked in the iOS 27 simulator**: the launch failure and the black screen behind SCN001 and
|
|
51
|
+
SCN002 were reproduced with Xcode 27.0, and the recordings are in the repository.
|
|
52
|
+
|
|
53
|
+
## Quick start
|
|
54
|
+
|
|
55
|
+
You need [Node.js](https://nodejs.org/) 22.12 or later (`node --version` prints it; on a Mac,
|
|
56
|
+
`brew install node` installs it). Nothing else.
|
|
57
|
+
|
|
58
|
+
**1. Open a terminal in your app's folder**, the one with the `.xcodeproj`, `pubspec.yaml` or
|
|
59
|
+
`package.json`:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
cd path/to/YourApp
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**2. Run sdkbump:**
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
npx @mcamdali/sdkbump
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The npm package is `@mcamdali/sdkbump`; the command it installs is `sdkbump`. An unscoped
|
|
72
|
+
`npx sdkbump` does not run it.
|
|
73
|
+
|
|
74
|
+
**3. Read the first line under your project's name.** "Fails to launch when built with the iOS 27
|
|
75
|
+
SDK" means the app stops at launch until you fix the **ERROR** findings; each one says which fix
|
|
76
|
+
from the "How to fix" list applies. **WARNING** findings do not stop the launch but stop working
|
|
77
|
+
once the app has a scene. "Launch not determined" means sdkbump could not read an Info.plist; the
|
|
78
|
+
notes at the end of the report say which one and why (see Limits below).
|
|
79
|
+
|
|
80
|
+
## What changes when you build with the iOS 27 SDK
|
|
81
|
+
|
|
82
|
+
From Apple's
|
|
83
|
+
[Transitioning to the UIKit scene-based life cycle](https://developer.apple.com/documentation/uikit/transitioning-to-the-uikit-scene-based-life-cycle):
|
|
84
|
+
|
|
85
|
+
> Beginning in iOS 27, iPadOS 27, Mac Catalyst 27, tvOS 27, and visionOS 27, apps built with the
|
|
86
|
+
> latest SDK must adopt the scene-based life cycle or they fail to launch.
|
|
87
|
+
|
|
88
|
+
1. **The app stops at launch if it declares no scene.** No `UIApplicationSceneManifest` with a
|
|
89
|
+
configuration in Info.plist and no `application(_:configurationForConnecting:options:)`: UIKit
|
|
90
|
+
ends the process with "UIScene life cycle is required for apps built with this SDK". The
|
|
91
|
+
compiler does not warn, because the check runs when the app starts. React Native 0.87 and
|
|
92
|
+
earlier, Expo SDK 57 without its opt-in and Expo SDK 56 and earlier create apps like this; so do
|
|
93
|
+
Flutter apps that the Flutter tool cannot migrate.
|
|
94
|
+
2. **With a scene, the app delegate stops receiving UI events.** Links, user activities, quick
|
|
95
|
+
actions and the active/background transitions go to the scene delegate, and a window the app
|
|
96
|
+
delegate creates is never shown.
|
|
97
|
+
|
|
98
|
+
[Why it compiles and still crashes](docs/guides/why-it-crashes.md) shows the
|
|
99
|
+
[launch matrix](examples/launch-matrix/README.md) we ran in the iOS 27.0 simulator.
|
|
100
|
+
|
|
101
|
+
## What sdkbump checks
|
|
102
|
+
|
|
103
|
+
| Rule | Severity | Finding | Seen in the iOS 27.0 simulator |
|
|
104
|
+
| ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
|
|
105
|
+
| SCN001 | error | No scene configuration: no configured `UIApplicationSceneManifest`, no `configurationForConnecting`, not a SwiftUI `App`; for Expo without `ios/`, the SDK version and `enableSceneSupport` | stops at launch |
|
|
106
|
+
| SCN002 | error | The app delegate creates the window with `UIWindow(frame:)` | black screen once the app has a scene |
|
|
107
|
+
| SCN003 | warning | App delegate methods and launch option keys UIKit no longer uses with scenes (13 of them), unless the scene delegate already handles them | `applicationDidEnterBackground(_:)` not called in a SwiftUI app |
|
|
108
|
+
| SCN004 | warning | Window lookups through `UIApplication.shared.keyWindow`, `.windows` or the app delegate's `window` | compiler deprecation warnings only |
|
|
109
|
+
|
|
110
|
+
Rule data `ios27@2026.10.15`, checked against Xcode 27.0 (27A266a). When React Native or Expo has
|
|
111
|
+
`react-native-uiscene-bridge` or `react-native-xcode27` installed, the findings it handles at run
|
|
112
|
+
time are shown as handled, with the limits from its README, and do not fail the run.
|
|
113
|
+
|
|
114
|
+
## Installation
|
|
115
|
+
|
|
116
|
+
Install the `sdkbump` command from npm (package `@mcamdali/sdkbump`):
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
npm i -g @mcamdali/sdkbump
|
|
120
|
+
sdkbump
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
In a project, `npm install --save-dev @mcamdali/sdkbump` pins a version, and `npx sdkbump` then
|
|
124
|
+
runs that copy. Each [GitHub release](https://github.com/muhittincamdali/sdkbump/releases) also
|
|
125
|
+
carries the same package as `sdkbump-<version>.tgz` with a `SHA256SUMS` file; `npx` runs it from
|
|
126
|
+
its URL without npm. A Homebrew formula for `muhittincamdali/tap` is
|
|
127
|
+
prepared by the release workflow (planned with the first release of the tap).
|
|
128
|
+
|
|
129
|
+
## Usage
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
sdkbump [path] [options]
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
| Option | What it does |
|
|
136
|
+
| ----------------------- | -------------------------------------------------------------------------- |
|
|
137
|
+
| `path` | Project folder or `.xcodeproj`; default is the current folder |
|
|
138
|
+
| `-f, --format <format>` | `tty` (default), `markdown` or `json` |
|
|
139
|
+
| `-o, --output <file>` | Write the report to a file instead of the terminal |
|
|
140
|
+
| `--fail-on <severity>` | `error` (default), `warning`, `info` or `none`: when the exit code is 1 |
|
|
141
|
+
| `--no-color` | Plain text even in a terminal (`NO_COLOR` and `FORCE_COLOR` are respected) |
|
|
142
|
+
|
|
143
|
+
Exit codes: `0` nothing unhandled at or above `--fail-on`, `1` findings at or above it, `2` bad
|
|
144
|
+
arguments, no iOS project found, or an unexpected error.
|
|
145
|
+
|
|
146
|
+
sdkbump finds the iOS app on its own: an `.xcodeproj` at the root or one folder down for native
|
|
147
|
+
apps, `ios/` for Flutter, React Native and Expo apps (`example/ios/` for a Flutter plugin). It
|
|
148
|
+
reads every iOS app target in the project file, the Info.plist each one names (XML or binary),
|
|
149
|
+
and the Swift and Objective-C files of the app, skipping Pods, `node_modules`, build output and
|
|
150
|
+
tests. Expo apps without `ios/` are checked from package.json and app.json.
|
|
151
|
+
|
|
152
|
+
### Keep a Markdown report with the code
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
npx @mcamdali/sdkbump --format markdown --output sdkbump.md
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Use the JSON report in scripts
|
|
159
|
+
|
|
160
|
+
```sh
|
|
161
|
+
npx @mcamdali/sdkbump --format json > sdkbump.json
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Each finding has `rule`, `severity`, `file`, `line`, `message`, the `fix` with its steps and guide,
|
|
165
|
+
`alternatives`, `mitigation` (set when a workaround package handles it) and `sources`. The layout
|
|
166
|
+
is `schemaVersion` 0 and may change before 1.0.
|
|
167
|
+
[examples/shell/launch-blockers.sh](examples/shell/launch-blockers.sh) filters it with `jq`.
|
|
168
|
+
|
|
169
|
+
### Run it in CI
|
|
170
|
+
|
|
171
|
+
[examples/github-actions/sdkbump.yml](examples/github-actions/sdkbump.yml) adds the report to the
|
|
172
|
+
job summary of every pull request that touches the iOS app and fails the job on errors.
|
|
173
|
+
|
|
174
|
+
### From JavaScript
|
|
175
|
+
|
|
176
|
+
```js
|
|
177
|
+
import { render, scan } from 'sdkbump';
|
|
178
|
+
|
|
179
|
+
const report = await scan('path/to/app');
|
|
180
|
+
console.log(report.verdict.text);
|
|
181
|
+
console.log(render(report, 'markdown'));
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
A runnable version is [examples/api/verdicts.mjs](examples/api/verdicts.mjs).
|
|
185
|
+
|
|
186
|
+
## Demo
|
|
187
|
+
|
|
188
|
+
The terminal recordings come from the CLI on the [fixtures](fixtures/README.md), real projects
|
|
189
|
+
made with each toolchain:
|
|
190
|
+
|
|
191
|
+
| Project | Recording |
|
|
192
|
+
| -------------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
193
|
+
| React Native 0.87.1 app | top of this page |
|
|
194
|
+
| Flutter app with a customised AppDelegate (Flutter 3.24) | [light](docs/media/demo-flutter-light.svg) · [dark](docs/media/demo-flutter-dark.svg) |
|
|
195
|
+
| SwiftUI app with an adaptor app delegate | [light](docs/media/demo-swiftui-light.svg) · [dark](docs/media/demo-swiftui-dark.svg) |
|
|
196
|
+
|
|
197
|
+
In the iOS 27.0 simulator (iPhone 17, built with Xcode 27.0), the native UIKit fixture stops at
|
|
198
|
+
launch and the same app moved to a scene delegate launches:
|
|
199
|
+
|
|
200
|
+
<picture><source media="(prefers-color-scheme: dark)" srcset="docs/media/demo-simulator-native-uikit-dark.svg"><source media="(prefers-color-scheme: light)" srcset="docs/media/demo-simulator-native-uikit-light.svg"><img src="docs/media/demo-simulator-native-uikit-light.svg" alt="scripts/simulator-check.sh native-uikit on the iOS 27.0 simulator: not running 4 seconds after launch, with UIKit's line: Application failed to launch: UIScene life cycle is required for apps built with this SDK"></picture>
|
|
201
|
+
|
|
202
|
+
<p align="center"><img src="docs/media/simulator/launched.png" width="480" alt="The native-uikit-scenes and native-swiftui fixtures running on an iPhone 17 simulator with iOS 27.0, built with Xcode 27.0"></p>
|
|
203
|
+
|
|
204
|
+
Measured on 15 October 2026 on a Mac (Apple silicon, M5 Max) with Node.js 26.11 while other jobs
|
|
205
|
+
were running on it: three sets of 100 runs of `node dist/cli/main.js` on the ten fixtures, Node.js
|
|
206
|
+
start-up included. The medians of the three sets were 0.11, 0.12 and 0.19 seconds; the slowest
|
|
207
|
+
run took 0.33 seconds. A busier machine gives longer times.
|
|
208
|
+
|
|
209
|
+
## Status
|
|
210
|
+
|
|
211
|
+
| Feature | Status |
|
|
212
|
+
| --------------------------------------------------------------------------------------------- | ------------- |
|
|
213
|
+
| Native, Flutter (app and plugin example), React Native and Expo project detection | works |
|
|
214
|
+
| SCN001 to SCN004 on the app's own Info.plist and Swift/Objective-C sources | works |
|
|
215
|
+
| Fix selection by ecosystem and version; Flutter auto-migration check; workaround packages | works |
|
|
216
|
+
| Terminal, Markdown and JSON reports; `--fail-on` exit codes | works |
|
|
217
|
+
| Dependencies: Pods, `node_modules` and Flutter plugin sources, with the package named | planned (0.2) |
|
|
218
|
+
| SCN005 workaround notice as its own rule; DPT001 deployment targets below 15.0 | planned (0.2) |
|
|
219
|
+
| `--verify`: build and launch in the iOS 27 simulator from the CLI | planned (0.3) |
|
|
220
|
+
| Swift/Objective-C syntax trees instead of patterns; built `.app` checks; SiriKit deprecations | planned (0.5) |
|
|
221
|
+
| `fix --dry-run` for native apps, SARIF output, GitHub Action | planned (0.8) |
|
|
222
|
+
| Stable rule schema and JSON layout | planned (1.0) |
|
|
223
|
+
|
|
224
|
+
## Limits
|
|
225
|
+
|
|
226
|
+
- **Patterns, not a compiler.** The rules are regular expressions over Swift and Objective-C text
|
|
227
|
+
whose comments and strings are blanked. Code that is generated, hidden behind macros or written
|
|
228
|
+
in an unusual shape can be missed or matched by mistake. Syntax trees are planned (0.5).
|
|
229
|
+
- **Only the app's own sources.** Pods, `node_modules`, Flutter plugins and other dependencies are
|
|
230
|
+
not read, so a plugin that relies on app delegate methods UIKit no longer calls is not reported.
|
|
231
|
+
Dependency checks are planned (0.2).
|
|
232
|
+
- **Build settings from project.pbxproj only.** sdkbump reads `INFOPLIST_FILE` and
|
|
233
|
+
`GENERATE_INFOPLIST_FILE` from the project file and expands `$(SRCROOT)`, `$(PROJECT_DIR)`,
|
|
234
|
+
`$(TARGET_NAME)` and `$(PRODUCT_NAME)`. It does not read `.xcconfig` files or expand other build
|
|
235
|
+
settings, and it does not read an Info.plist outside the project folder. When it cannot read a
|
|
236
|
+
target's Info.plist, SCN001 is an info finding and the verdict is "Launch not determined"
|
|
237
|
+
instead of a guess.
|
|
238
|
+
- **No suppression file.** A finding cannot be marked as reviewed; `--fail-on` decides which
|
|
239
|
+
severities fail the run.
|
|
240
|
+
- **Files only.** sdkbump does not build or launch the app; `--verify` is planned (0.3).
|
|
241
|
+
|
|
242
|
+
## Documentation
|
|
243
|
+
|
|
244
|
+
API reference and guides: [muhittincamdali.github.io/sdkbump](https://muhittincamdali.github.io/sdkbump/).
|
|
245
|
+
|
|
246
|
+
## Guides
|
|
247
|
+
|
|
248
|
+
- [Why it compiles and still crashes](docs/guides/why-it-crashes.md)
|
|
249
|
+
- [Native UIKit and SwiftUI apps](docs/guides/native-ios.md)
|
|
250
|
+
- [Flutter: a custom AppDelegate and plugins](docs/guides/flutter.md)
|
|
251
|
+
- [React Native before 0.88 and Expo before SDK 58](docs/guides/react-native-expo.md)
|
|
252
|
+
- [Native module authors: links and notifications at cold start](docs/guides/native-module-authors.md)
|
|
253
|
+
|
|
254
|
+
## Related tools
|
|
255
|
+
|
|
256
|
+
Other projects work on the same change; which one fits depends on what you need:
|
|
257
|
+
|
|
258
|
+
- [react-native-uiscene-bridge](https://github.com/Atomics-hub/react-native-uiscene-bridge) and
|
|
259
|
+
[react-native-xcode27](https://github.com/Atomics-hub/react-native-xcode27) make a React Native
|
|
260
|
+
or Expo app launch without code changes, at run time. They fit when you cannot upgrade yet;
|
|
261
|
+
sdkbump recognises them.
|
|
262
|
+
- [auditor-uiscene](https://github.com/Anzell/auditor-uiscene) audits Flutter apps and their
|
|
263
|
+
plugins with a shell script.
|
|
264
|
+
- Flutter's own migrator (`flutter build ios` from 3.41) migrates an unmodified Flutter app.
|
|
265
|
+
- [flutter_spm_doctor](https://pub.dev/packages/flutter_spm_doctor) checks Flutter plugins.
|
|
266
|
+
|
|
267
|
+
## Requirements
|
|
268
|
+
|
|
269
|
+
| Toolchain | Minimum version |
|
|
270
|
+
| --------- | --------------- |
|
|
271
|
+
| Node.js | 22.12 |
|
|
272
|
+
|
|
273
|
+
sdkbump does not need Xcode: it reads files, so it also runs on Linux. CI runs the tests on
|
|
274
|
+
Node.js 22 and 24 (Linux); locally all 280 pass on Node.js 22.23, 24.22 and 26.11 on a Mac (Apple
|
|
275
|
+
silicon). The simulator recordings need a Mac with Xcode 27 and are not part of CI.
|
|
276
|
+
|
|
277
|
+
## Architecture
|
|
278
|
+
|
|
279
|
+
```text
|
|
280
|
+
path ─► detectProject native | flutter | react-native | expo; iOS folder; versions
|
|
281
|
+
─► collectApp iOS app targets from project.pbxproj, their Info.plist (XML or binary),
|
|
282
|
+
Swift and Objective-C sources with comments and strings blanked
|
|
283
|
+
─► checkApp SCN001…SCN004, fix per ecosystem and version, workaround packages
|
|
284
|
+
rules/ios27.json what each rule says, fixes, sources (validated with zod)
|
|
285
|
+
─► render tty | markdown | json
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
- Detection patterns are regular expressions over source text whose comments and strings are
|
|
289
|
+
blanked, and only count inside an app delegate or scene delegate where that matters.
|
|
290
|
+
- The rule data is separate from the code and versioned (`ios27@2026.10.15`); a test fails when a
|
|
291
|
+
rule, fix or workaround has no source.
|
|
292
|
+
- The CLI (`src/cli/`) only parses arguments, picks colours and widths, and sets the exit code.
|
|
293
|
+
|
|
294
|
+
## Roadmap
|
|
295
|
+
|
|
296
|
+
See the [Status](#status) table: 0.2 adds dependencies and deployment targets, 0.3 launching in
|
|
297
|
+
the simulator from the CLI, 0.5 syntax trees and built-app checks, 0.8 fixes and SARIF, 1.0 a
|
|
298
|
+
stable schema. [CHANGELOG.md](CHANGELOG.md) lists what each release changed.
|
|
299
|
+
|
|
300
|
+
## FAQ
|
|
301
|
+
|
|
302
|
+
**My app runs on iOS 27 today. Is it affected?** If it was built with Xcode 26 or earlier, the
|
|
303
|
+
check does not apply to that build: in our run the same code linked against the iOS 26 SDK
|
|
304
|
+
launched on the iOS 27 simulator. It applies to your next build with Xcode 27.
|
|
305
|
+
|
|
306
|
+
**Does sdkbump replace testing on a device?** No. It reads files. The simulator recordings in this
|
|
307
|
+
repository are from the iOS 27.0 simulator; nothing here was run on a device.
|
|
308
|
+
|
|
309
|
+
**Why is a finding "handled"?** A workaround package in package.json (react-native-uiscene-bridge
|
|
310
|
+
or react-native-xcode27) declares a scene at run time. The app launches, but it still has no scene
|
|
311
|
+
support of its own; the report lists the package's limits and the real fix.
|
|
312
|
+
|
|
313
|
+
**Does sdkbump change my project?** No. It reads it and writes only the report you ask for.
|
|
314
|
+
|
|
315
|
+
## Contributing
|
|
316
|
+
|
|
317
|
+
Contributions are welcome: read [CONTRIBUTING.md](CONTRIBUTING.md) and the
|
|
318
|
+
[code of conduct](https://github.com/muhittincamdali/.github/blob/main/CODE_OF_CONDUCT.md) before
|
|
319
|
+
opening a pull request, and report security issues as described in [SECURITY.md](SECURITY.md).
|
|
320
|
+
|
|
321
|
+
## License
|
|
322
|
+
|
|
323
|
+
Released under the MIT license. See [LICENSE](LICENSE).
|