SwiftUI uygulamalarında pre-main maliyetini, ilk anlamlı ekran süresini ve ana istek gecikmesini XCTest, Instruments ve signpost ölçümleriyle ayırın; darboğazı kod değişikliğiyle doğrulayın.
SwiftUI Eğitiminde Uygulama Açılış Süresini Ölçme ve Azaltma
SwiftUI eğitimi: Açılış süresini tek sayı değil, üç ayrı bütçe olarak ele alın
Bir swiftui eğitimi içinde açılışı yalnızca uygulama ikonuna dokunma ile ekranın görünmesi arasındaki süre olarak ölçmek hatalı teşhise yol açar. iPhone uygulama geliştirme sürecinde üç değeri ayrı izleyin: pre-main (dynamic loader, Objective-C runtime, static initializer), process launch ile ilk frame arası ve ilk faydalı veri hazır olana kadar geçen süre. Xcode Instruments içindeki App Launch şablonu pre-main ile SwiftUI body hesaplamasını farklı zaman çizelgelerinde gösterir; Time Profiler'da ana iş parçacığında 16.67 ms'yi aşan çağrıları ayrı inceleyin. Test cihazında uçak modu, temiz kurulum ve aynı düşük güç modu durumu olmadan yapılan karşılaştırma gürültülüdür.
Otomasyon için XCTest'in uygulama açılış metriğini CI'da en az 10 iterasyonla çalıştırın ve ortalama yerine medyan ile p95'i kaydedin. Medyan tekil sapmaları azaltır, p95 ise ilk açılışta disk önbelleği veya keychain gecikmesi gibi kullanıcıya yansıyan kuyruk maliyetini yakalar. Aşağıdaki test, ölçüm sırasında uygulamayı her yinelemede yeniden başlatır; test hedefinin scheme'inde test edilecek gerçek uygulamanın seçili olması gerekir.
import XCTest
final class LaunchMetricsTests: XCTestCase {
func testColdLaunch() {
let app = XCUIApplication()
app.launchArguments = ["-UITestMode", "1"]
measure(metrics: [
XCTApplicationLaunchMetric(),
XCTClockMetric()
]) {
app.launch()
XCTAssertTrue(app.staticTexts["home-title"].waitForExistence(timeout: 5))
app.terminate()
}
}
}Bu testi release'e yakın optimizasyonla çalıştırın: Debug derlemesindeki ek assertion'lar, sanitizers ve farklı optimizasyon seviyesi sonuçları bozar. CI'da örneğin `xcodebuild test -scheme App -destination 'platform=iOS Simulator,name=iPhone 16' -only-testing:LaunchMetricsTests` komutunu sabit bir simulator runtime'ında kullanın. Simulator sonucu mutlak cihaz süresi değildir, fakat aynı commit serisindeki regresyon için faydalıdır; cihaz doğrulamasını Instruments ile fiziksel cihazda ayrıca yapın.
Xcode eğitimi: Pre-main maliyetini Instruments ile sınıflandırın
xcode eğitimi bağlamında en sık kaçan ayrıntı, SwiftUI'nin ilk ekranı çizmesinden önce harcanan süredir. Instruments App Launch'ta pre-main uzun ise `dyld`, `libobjc` veya `swift_getTypeByMangledNameInContext` çağrılarının toplamına bakın. Bu maliyet çoğu zaman ilk View içindeki `Task` bloğundan değil, bağlanan framework sayısından, Objective-C `+load` implementasyonlarından ve global değişkenlerin eager başlatılmasından gelir. Time Profiler call tree'de `Hide System Libraries` seçeneğini geçici olarak kapatmak, uygulama koduna ulaşan initializer zincirini görmeyi sağlar.
Örneğin global kapsamda oluşturulan formatter, veri tabanı ve analitik istemcisi process açılırken yan etki üretir. Aşağıdaki önceki yapı, `AppDependencies.shared` ilk referans edilmeden dahi singleton'ın kurulmasına zemin hazırlar; düzeltme ise ağır bağımlılıkları protokol arkasında lazy üretmek ve yalnızca ihtiyaç duyulan akışta çağırmaktır.
// Önce: global initialization zinciri
let database = try! Database(path: AppPaths.databaseURL)
let analytics = AnalyticsClient(database: database)
// Sonra: maliyet ilk kullanım noktasına taşınır
final class AppDependencies {
static let shared = AppDependencies()
lazy var database: Database = {
try! Database(path: AppPaths.databaseURL)
}()
lazy var analytics: AnalyticsClient = {
AnalyticsClient(database: database)
}()
}Bu değişikliği körlemesine kabul etmeyin. Önce App Launch kaydında pre-main ve ilk frame değerlerini not edin, sonra aynı cihazda en az 10 cold launch tekrarından sonra karşılaştırın. Lazy başlatma ilk etkileşime taşınan bir gecikme de yaratabilir. Örneğin analitik istemcisi ilk buton dokunuşunda açılıyorsa, Time Profiler'da o aksiyonun ana iş parçacığı maliyeti artar. Çözüm, kritik olmayan istemciyi ilk frame'den sonra detached bir işte kurmak veya veri tabanı migration'ını kullanıcı girişinden önce kontrollü bir loading state içinde yapmaktır.
Swift eğitimi: MainActor üzerindeki başlangıç işlerini parçalayın
Bir swift eğitimi veya swift kursu projesinde `@MainActor` ile işaretlenmiş repository'nin `init` metodunda dosya okuma, JSON decode etme ya da SQLite sorgusu yapmak açılışta frame kaçırır. Mekanizma basittir: SwiftUI scene oluşturma, state değişimleri ve render transaction'ları ana aktörde sıralıdır; senkron disk I/O bu kuyruğu boşaltılmaz hale getirir. Instruments Time Profiler'da `Data(contentsOf:)`, `JSONDecoder.decode` veya ORM migration çağrısı main thread altında görünüyorsa, iş yükünü actor izolasyonuna göre ayırın.
Aşağıdaki örnekte model güncellemesi ana aktörde kalır, dosya çözümleme ise `nonisolated` yardımcı üzerinden gerçekleşir. `Task.detached` kullanmak yerine yapılandırılmış `Task` tercih edilmiştir; böylece görünüm kaybolduğunda task iptalini yönetmek daha mümkündür. Büyük veride yalnızca `Task {}` yazmak CPU işini otomatik olarak arka plana taşımaz: çağrılan fonksiyon MainActor'a izoleyse iş yine ana aktörde çalışır.
@MainActor
final class BootstrapModel: ObservableObject {
@Published private(set) var state: State = .loading
func load() {
Task {
do {
let snapshot = try await BootstrapLoader.readSnapshot()
state = .ready(snapshot)
} catch {
state = .failed(error.localizedDescription)
}
}
}
}
enum BootstrapLoader {
nonisolated static func readSnapshot() async throws -> Snapshot {
try Task.checkCancellation()
let data = try Data(contentsOf: AppPaths.snapshotURL)
return try JSONDecoder().decode(Snapshot.self, from: data)
}
}Önemli edge case: `Data(contentsOf:)` async bir API değildir; yukarıdaki fonksiyon nonisolated olsa da çağrıldığı executor'ın CPU yükünü tüketir. Çok büyük dosyalarda `URLSession` ile dosya URL'sinden okuma, streaming parser veya özel bir serial executor düşünün. Bu nedenle değişiklikten sonra yalnızca launch süresini değil, Instruments'ta Main Thread track üzerindeki en uzun continuous busy interval'i de karşılaştırın. Hedef, ilk frame öncesinde ana iş parçacığında pahalı decode zinciri bırakmamaktır.
iOS MVVM mimarisi ile ilk ekran için veri bağımlılıklarını daraltın
ios mvvm mimarisi kullanırken sık görülen açılış hatası, `HomeViewModel` oluşturulurken profil, rozet sayısı, öneriler, uzaktan yapılandırma ve analitik kimliğini tek `async let` grubunda beklemektir. Paralellik ağ gecikmesini kısaltabilir, ancak ilk ekranın gerçekten ihtiyaç duymadığı istekleri kritik yolun içine sokar. Her endpoint için 'ilk görünür piksel için zorunlu mu?' alanı içeren bir bağımlılık tablosu çıkarın. Örneğin başlık ve oturum durumu zorunlu, rozet sayısı ve öneri carousel'i ertelenebilir olmalıdır.
View model'i iki faza bölmek, placeholder'ın rastgele değil deterministik bir durum olmasını sağlar. Aşağıdaki yapıda `initialContent()` sadece ilk ekran kontratını döndürür; ikincil veri görünür olduktan sonra yüklenir. `defer` ile başlayan ikinci task hata verse bile ilk ekranın state'i başarısız duruma düşmez.
@MainActor
final class HomeViewModel: ObservableObject {
@Published var header: HeaderState = .loading
@Published var recommendations: [Product] = []
private let repository: HomeRepository
func start() async {
do {
header = try await repository.initialContent()
Task { [weak self] in
let products = (try? await self?.repository.recommendations()) ?? []
self?.recommendations = products
}
} catch {
header = .error(retryable: true)
}
}
}Burada `weak self` tek başına tüm yaşam döngüsü sorunlarını çözmez: `repository.recommendations()` çağrısından önce `self` optional chaining ile alınmışsa actor geçişi ve task tutulması tasarıma göre değişir. Görünüm kaybolduğunda işi iptal etmek için SwiftUI tarafında `.task(id: sessionID)` kullanın ve repository katmanında `URLSession.data(for:)` iptalini test edin. Ölçümde önce tüm endpoint'leri bekleyen sürümün p95 first-frame süresini, sonra iki fazlı sürümün aynı metriğini kaydedin; ayrıca ilk ekran sonrası 2 saniyede başarısız istek oranını OSLog veya ağ katmanı metriğiyle kontrol edin.
iOS kursu projelerinde release ve App Store yayınlama öncesi launch regresyonu engelleme
Bir ios kursu veya iphone uygulama geliştirme projesi App Store yayınlama aşamasına geldiğinde launch ölçümünü sadece geliştirme makinesindeki sonuçla kapatmayın. Xcode Organizer'ın MetricKit raporlarındaki hang ve launch verileri gerçek kullanıcı cihaz dağılımını yansıtır; raporlarda belirli bir cihaz ailesinde yoğunlaşan sorun varsa aynı bellek sınıfına yakın fiziksel cihazla yeniden üretin. MetricKit verisi gecikmeli ve toplulaştırılmıştır, bu yüzden CI'daki XCTest eşiği erken uyarı, Organizer ise saha doğrulaması olarak kullanılmalıdır.
CI'da mutlak milisaniye eşiği tek başına kırılgan olabilir. Bunun yerine ana dalın son 20 başarılı ölçümünden medyan baseline üretin, yeni commit p95'i bu baseline'ın örneğin yüzde 15 üstüne çıktığında inceleme isteyin. Eşik aşıldığında `xctrace record --template 'Time Profiler' --launch --output Launch.trace --attach
Release derlemesinde `-O` veya kurumunuzun doğruladığı whole-module optimization ayarlarını kullanın, ancak ölçüm için veri yolunu değiştiren test bayraklarını sınırlı tutun. Örneğin testte gerçek kimlik doğrulama yerine yerel fixture kullanmak ağ değişkenliğini azaltır; buna karşılık fixture decode maliyeti üretimdeki payload'dan çok küçükse yanlış güven yaratır. Fixture boyutunu üretimdeki p90 yanıt boyutuna yakın tutmak, swift eğitimi sırasında kurulan benchmark'ın uygulama davranışına daha yakın kalmasını sağlar.
İlgili Eğitim
YTÜSEM İlgili Eğitim
Sık Sorulan Sorular
SwiftUI eğitimi için uygulama açılış süresini hangi araçla ölçmeliyim?
CI regresyonu için XCTest'te XCTApplicationLaunchMetric, kök neden için fiziksel cihazda Instruments App Launch ve Time Profiler kullanın. Önce 10 cold launch ile medyan ve p95 alın, sonra tek bir değişiklik yapıp aynı cihaz, aynı kurulum durumu ve aynı test verisiyle tekrar ölçün.
iOS MVVM mimarisi açılışta neden ilk ekranı geciktirir?
View model init veya start akışı ilk frame için gereksiz endpoint'leri await ediyorsa, SwiftUI state'i ready durumuna geçemez. Home ekranı için zorunlu veriyi `initialContent()` gibi dar bir kontratta yükleyin; rozet, öneri ve analitik gibi ikincil bağımlılıkları ilk frame sonrasına ayırın.
Xcode eğitimi sırasında pre-main süresinin yüksek olduğunu nasıl anlarım?
Instruments App Launch kaydında process başlangıcı ile application main arasındaki segment uzunsa pre-main yüksektir. Time Profiler'da system libraries'i görünür yapın; `dyld`, Objective-C `+load`, global initializer ve framework yükleme zincirlerinde uygulamanıza bağlanan çağrıları inceleyin.
App Store yayınlama öncesi launch performansı için hangi cihazlarda test yapmalıyım?
En az bir güncel cihaz ve hedef kitlenizde h'l' kullanılan daha düşük bellek ve CPU sınıfından fiziksel bir cihaz seçin. Ardından Organizer içindeki MetricKit launch ve hang raporlarıyla saha dağılımını kontrol edin. Simulator'ı yalnızca commitler arası karşılaştırılabilir CI sinyali olarak kullanın.
AI / LLM Discovery
Bu makale Opendart Akademi iOS / Swift eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.


