İçindekiler
- Problem
- Neyi denetliyor
- Tasarım kararları ve alternatifler
- Gerçek bir hata: hiç okumadığı dosya hakkında tahmin
- Kullanım
- Sınırlar
Problem
Apple'ın Transitioning to the UIKit scene-based life cycle sayfası durumu tek cümleyle anlatıyor:
Beginning in iOS 27, iPadOS 27, Mac Catalyst 27, tvOS 27, and visionOS 27, apps built with the latest SDK must adopt the scene-based life cycle or they fail to launch.
Yani iOS 27 ile birlikte en son SDK'yla derlenen uygulamalar scene tabanlı yaşam döngüsüne geçmek zorunda; geçmeyenler açılmıyor. İşin zor yanı, hatanın nerede ortaya çıktığı. Info.plist'inde yapılandırılmış bir UIApplicationSceneManifest olmayan ve application(_:configurationForConnecting:options:) metodunu uygulamayan bir uygulama, Xcode 27 ile tek bir uyarı bile almadan derleniyor. Kontrol uygulama açılırken UIKit'in içinde çalışıyor ve süreç "UIScene life cycle is required for apps built with this SDK" mesajıyla sonlanıyor. React Native 0.87 ve öncesi, opt-in'i açılmamış Expo SDK 57, Expo SDK 56 ve öncesi ile Flutter aracının otomatik taşıyamadığı Flutter uygulamaları tam olarak bu şekilde oluşturuluyor.
Bir de daha sessiz bir değişiklik var. Uygulamada bir scene olduğu anda app delegate arayüz olaylarını almayı bırakıyor: linkler, user activity'ler, quick action'lar ve active/background geçişleri scene delegate'e gidiyor. App delegate'in oluşturduğu pencere hiçbir scene'e ait olmadığı için de hiç gösterilmiyor.
sdkbump, bir projenin dosyalarını okuyup ilk soruyu test cihazından önce cevaplayan bir komut satırı aracı: bu uygulama iOS 27 SDK'sıyla derlendiğinde açılacak mı? Ardından projeye uyan düzeltmeyi söylüyor ve uygulama scene'e geçtiğinde çalışmayı bırakacak app delegate kodunu listeliyor.
Neyi denetliyor
Kurallar SCN001–SCN004:
- SCN001 (error): scene yapılandırması yok. Yapılandırılmış
UIApplicationSceneManifestyok,configurationForConnectingyok, SwiftUIAppdeğil.ios/klasörü olmayan bir Expo uygulamasında bu, SDK sürümünden veios.enableSceneSupportayarından çıkarılıyor. - SCN002 (error): app delegate pencereyi
UIWindow(frame:)ile oluşturuyor; scene olduğu anda ekran siyah kalıyor. - SCN003 (warning): UIKit'in scene varken artık kullanmadığı 13 app delegate metodu ve launch option anahtarı; scene delegate bunları zaten karşılıyorsa raporlanmıyor.
- SCN004 (warning):
UIApplication.shared.keyWindow,.windowsya da app delegate'inwindowözelliği üzerinden pencere aramaları.
Her kural iOS 27.0 simülatöründe sınandı. Repoda dokuz minimal uygulamadan oluşan bir launch matrix var; uygulamalar swiftc ile derlenip Xcode 27.0 (27A266a) ile iPhone 17 simülatöründe açılıyor. A, B ve I satırları açılışta kapanıyor; C (configurationForConnecting kodda) ve D (SwiftUI App) tek pencereyle açılıyor; E, G ve H görünür pencere olmadan açılıyor. F satırı A ile aynı kod ama SDK 26.0'a karşı linkleniyor ve açılıyor. Yani kontrol cihazın iOS sürümüne değil, uygulamanın hangi SDK'yla linklendiğine bağlı.
Tasarım kararları ve alternatifler
Akış kısa; README onu şöyle çiziyor:
text
1path ─► detectProject native | flutter | react-native | expo; iOS folder; versions2 ─► collectApp iOS app targets from project.pbxproj, their Info.plist (XML or binary),3 Swift and Objective-C sources with comments and strings blanked4 ─► checkApp SCN001…SCN004, fix per ecosystem and version, workaround packages5 rules/ios27.json what each rule says, fixes, sources (validated with zod)6 ─► render tty | markdown | jsonDerlemek yerine dosya okumak. En kesin cevabı uygulamayı derleyip simülatörde açmak verir. Ama bunun için Mac, Xcode 27 ve her koşu için dakikalar gerekir. Dosya okumak ise yalnız Node.js 22.12 istiyor, Linux CI makinelerinde de çalışıyor ve projeyi değiştiremiyor. CLI'dan derleyip açmak yol haritasında --verify olarak (0.3) duruyor.
Kurallar veri olarak. Her kuralın metni, her düzeltme, her app delegate hook'u ve her workaround paketi rules/ios27.json dosyasında duruyor ve zod ile doğrulanıyor. Her kayıt Apple, Flutter, React Native ya da Expo'dan bir kaynak URL'si taşıyor; biri eksikse test kırılıyor. Verinin kendi sürümü var (ios27@2026.10.15) ve her raporda yazıyor; okuyan kişi kuralların ne kadar eski olduğunu görebiliyor. Metinleri koda gömmek daha kolay olurdu, ama o zaman ne kaynakları ne de tazelikleri denetlenebilirdi.
Syntax tree değil, temizlenmiş kaynak üzerinde desenler. Dedektörler, yorumları ve string içerikleri boşlukla değiştirilmiş Swift ve Objective-C metni üzerinde çalışan düzenli ifadeler. Satır sonları ve offset'ler aynı kalıyor; böylece yoruma alınmış bir application(_:open:options:) eşleşmiyor ve satır numaraları doğru yeri gösteriyor. Sınıf bölgeleri süslü parantez eşleştirmesiyle bulunuyor, böylece bir eşleşme yalnız gerektiği yerde, yani app delegate ya da scene delegate içinde sayılıyor. Syntax tree (tree-sitter ya da SwiftSyntax) daha doğru olurdu ama WASM ya da native bir bağımlılık getirirdi. README bu ödünleşimi Limits bölümünde açıkça yazıyor; syntax tree 0.5'e planlı. Bugünkü runtime bağımlılıkları yalnız commander ve zod.
Ekosisteme ve sürüme göre düzeltme. "Scene'e geç" herkes için aynı talimat değil. React Native'de 0.88 ya da elle yazılmış bir scene delegate; Expo'da SDK 57'nin opt-in'i ya da SDK 58; Flutter'da Flutter'ın kendi koşulları sağlanıyorsa aracın otomatik taşıması, sağlanmıyorsa elle adımlar; SwiftUI uygulamasında SwiftUI API'si. sdkbump sürümleri package.json, pubspec.yaml ya da FVM yapılandırmasından okuyup buna göre seçiyor.
Workaround'lar yok sayılmıyor, tanınıyor. react-native-uiscene-bridge ve react-native-xcode27, bir React Native ya da Expo uygulamasını kod değiştirmeden çalışma anında açılır hale getiriyor. Bunlardan biri kuruluysa, karşıladığı bulgular paketin README'sindeki sınırlarla birlikte "handled" olarak gösteriliyor ve koşuyu kırmıyor. Hüküm, uygulamanın bu paket sayesinde açıldığını ve kendisinin scene'e geçmediğini söylüyor.
Denetlenen proje güvenilmeyen girdidir. Dosya adları, plist değerleri, target adları ve INFOPLIST_FILE yolu taranan projeden geliyor; bu proje başka birinin açtığı bir pull request olabilir. Proje dosyaları yalnız proje klasörünün içindeki normal dosyalarsa okunuyor (src/core/files.ts içindeki readProjectFile). 0.1.3'ten beri render(), raporun her string'indeki kontrol karakterlerini herhangi bir biçime geçmeden önce bir kez değiştiriyor; Markdown çıktısına da link, görsel, HTML, başlık ya da tablo hücresi eklenemiyor.
Gerçek bir hata: hiç okumadığı dosya hakkında tahmin
0.1.0 ve 0.1.1 sürümlerinde yanlış bir hüküm vardı. sdkbump bir target'ın Info.plist'ini okuyamadığında, dosyayı Xcode üretiyormuş gibi davranıyordu: SCN001 "The Info.plist Xcode generates has no UIApplicationSceneManifest" diyordu, hüküm "Fails to launch" ve çıkış kodu 1 oluyordu. Bu durum INFOPLIST_FILE bir .xcconfig dosyasında tanımlandığında, sdkbump'ın genişletmediği bir build setting kullandığında ($(CUSTOM_DIR)/Info.plist), var olmayan bir dosyayı gösterdiğinde ya da proje dışına giden bir link olduğunda ortaya çıkıyordu. Bu uygulamaların scene manifest'i ve scene delegate'i olabiliyordu ve GENERATE_INFOPLIST_FILE değeri NO idi; yani hem hüküm hem de ifadesi yanlıştı. CI'da bu, gayet açılan bir uygulamanın pull request'ini kırmızıya düşürürdü.
Düzeltme PR #31, commit 25751e3 ("fix(scan): do not report a launch failure when the Info.plist was not read") ile geldi ve 0.1.2'de yayınlandı. Artık her target, Info.plist'inin nereden geldiğini infoPlistSource alanında tutuyor: file, generated ya da not-read. configurationForConnecting uygulamayan not-read bir target, SCN001 info bulgusu ("Scene configuration not checked") ve unknown hüküm kodu alıyor; metni "Launch not determined: sdkbump could not read the Info.plist of …". --fail-on info verilmedikçe koşu 0 ile çıkıyor. INFOPLIST_FILE belirtmeyen ve Info.plist'i Xcode'a da ürettirmeyen bir target'a, ayarların bir .xcconfig dosyasından gelebileceğini söyleyen bir not ekleniyor.
Buradan çıkan ders statik analizin geneli için geçerli: emin görünen yanlış bir cevap, "belirlenemedi" demekten daha pahalı. Aynı sürüm README'ye ve çevirilerine bir Limits bölümü ekledi; hızlı başlangıç da artık "Launch not determined" hükmünü açıklıyor.
Aynı türden daha erken bir değişiklik 0.1.0 changelog'unda kayıtlı. İlk planda SCN002, "application(_:configurationForConnecting:options:) yok" anlamına geliyordu. Launch matrix bu metodun tek başına açılış için yettiğini gösterdi (C satırı). Bu yüzden durum SCN001'in bir parçası oldu; SCN002 ise app delegate'te oluşturulan pencereye, yani E, G ve H satırlarındaki siyah ekrana dönüştü.
Kullanım
Uygulamanın klasöründe (.xcodeproj, pubspec.yaml ya da package.json dosyasının olduğu yerde) bir terminal açıp çalıştırın:
sh
1npx @mcamdali/sdkbumpnpm paketinin adı @mcamdali/sdkbump; kurduğu komut ise sdkbump. Scope'suz npx sdkbump bu aracı çalıştırmaz. Proje adının altındaki ilk satır hükümdür. "Fails to launch when built with the iOS 27 SDK", ERROR bulguları düzeltilene kadar uygulamanın açılışta kapanacağı anlamına gelir; her bulgu "How to fix" listesinden hangi düzeltmenin uyduğunu söyler.
Tam biçim şöyle:
text
1sdkbump [path] [options]Kodun yanında bir Markdown raporu tutmak için:
sh
1npx @mcamdali/sdkbump --format markdown --output sdkbump.mdBetikler için JSON raporu:
sh
1npx @mcamdali/sdkbump --format json > sdkbump.jsonJSON raporundaki her bulguda rule, severity, file, line, message, adımları ve rehberiyle fix, alternatives, mitigation ve sources alanları var. Yapı schemaVersion 0 ve 1.0'dan önce değişebilir. Çıkış kodları: --fail-on seviyesinde (varsayılan error) ya da üstünde karşılanmamış bulgu yoksa 0, varsa 1; hatalı argüman, bulunamayan iOS projesi ya da beklenmeyen bir hata için 2. Repoda raporu job özetine ekleyen bir GitHub Actions örneği ve JSON'u jq ile süzen bir shell örneği var. JavaScript tarafında paket scan ve render fonksiyonlarını dışa açıyor; examples/api/verdicts.mjs çalıştırılabilir bir örnek ve test paketi tarafından da koşturuluyor.
v0.1.3'te test paketinde 18 dosyada 280 test var; temiz bir klonda npx vitest run ile hepsi geçiyor. CI bunları Linux'ta Node.js 22 ve 24 üzerinde koşturuyor; README yerel koşular için 22.23, 24.22 ve 26.11 sürümlerini listeliyor.
Sınırlar
README, sdkbump'ın neyi yapmadığını açıkça yazıyor; temiz bir raporu okurken bu sınırlar önemli:
- Derleyici değil, desen. Üretilmiş kod, makroların arkasındaki kod ya da alışılmadık biçimde yazılmış kod gözden kaçabilir ya da yanlışlıkla eşleşebilir.
- Yalnız uygulamanın kendi kaynakları. Pods,
node_modulesve Flutter plugin'leri okunmuyor; UIKit'in artık çağırmadığı app delegate metotlarına dayanan bir plugin raporlanmıyor. Bağımlılık denetimi 0.2'ye planlı. - Build setting'ler yalnız project.pbxproj'dan. sdkbump
$(SRCROOT),$(PROJECT_DIR),$(TARGET_NAME)ve$(PRODUCT_NAME)değişkenlerini genişletiyor;.xcconfigdosyalarını ve proje klasörü dışındaki bir Info.plist'i okumuyor. - Bastırma dosyası yok. Bir bulgu "incelendi" olarak işaretlenemiyor; hangi seviyelerin koşuyu kıracağına
--fail-onkarar veriyor. - Yalnız dosyalar. Hiçbir şey derlenmiyor ya da açılmıyor; repodaki kayıtlar cihazdan değil, iOS 27.0 simülatöründen.
Kurallar Xcode 27.0'ı (27A266a) anlatıyor. Apple davranışı sonraki bir sürümde değiştirirse kural verisinin de güncellenmesi gerekir; sürümünün her raporda yazmasının nedeni bu. JSON yapısı ve kural şeması 1.0'dan önce kararlı değil.
Kaynak kod: github.com/muhittincamdali/sdkbump

