App launch time, kullanıcı deneyiminin en kritik metriklerinden biridir. Apple'a göre, kullanıcıların %25'i 3 saniyeden uzun süren uygulamaları terk ediyor. Bu rehberde, launch time'ı dramatik şekilde iyileştirmenin yollarını keşfedeceğiz.
İçindekiler
- Launch Time Anatomisi
- Dylib Optimizasyonu
- İlk Ekran Optimizasyonu
- Instruments ile Profiling
- Launch Time Regression Tespiti
- Checklist
- Sonuç
Launch Time Anatomisi
iOS'ta app launch iki ana fazdan oluşur:
Pre-main Phase
Dylib'lerin yüklenmesi, Objective-C runtime setup, +load metodları:
swift
1// ❌ KÖTÜ - +load metodları pre-main'i yavaşlatır2@objc class LegacyManager: NSObject {3 override class func load() {4 // Bu kod main() öncesi çalışır - KAÇININ!5 setupLogging()6 configureAnalytics()7 }8}9 10// ✅ İYİ - Lazy initialization11@objc class ModernManager: NSObject {12 static let shared = ModernManager()13 14 private init() {15 // İlk erişimde çalışır16 setupLogging()17 configureAnalytics()18 }19}Post-main Phase
AppDelegate/SceneDelegate, ilk UI render:
swift
1// ❌ KÖTÜ - Senkron initialization2@main3struct MyApp: App {4 init() {5 // Ana thread'i bloklayan işlemler6 FirebaseApp.configure() // ~200ms7 Crashlytics.configure() // ~100ms8 Analytics.configure() // ~150ms9 // Toplam: ~450ms bekleme!10 }11 12 var body: some Scene {13 WindowGroup {14 ContentView()15 }16 }17}18 19// ✅ İYİ - Async initialization20@main21struct MyApp: App {22 @State private var isInitialized = false23 24 init() {25 // Sadece kritik setup26 configureAppearance()27 }28 29 var body: some Scene {30 WindowGroup {31 if isInitialized {32 ContentView()33 } else {34 SplashView()35 .task {36 await initializeServices()37 isInitialized = true38 }39 }40 }41 }42 43 private func initializeServices() async {44 // Parallel initialization45 async let firebase = Task { FirebaseApp.configure() }46 async let crash = Task { Crashlytics.configure() }47 async let analytics = Task { Analytics.configure() }48 49 _ = await (firebase, crash, analytics)50 }51}Dylib Optimizasyonu
Framework Sayısını Azaltın
swift
1// Xcode -> Build Settings -> 2// "Dead Code Stripping" = YES3// "Link-Time Optimization" = YES4 5// Package.swift - Sadece gereken modüller6dependencies: [7 // ❌ Tüm Firebase8 // .package(url: "firebase-ios-sdk", from: "10.0.0"),9 10 // ✅ Sadece gerekli modüller11 .product(name: "FirebaseAnalytics", package: "firebase-ios-sdk"),12 .product(name: "FirebaseAuth", package: "firebase-ios-sdk"),13]Static vs Dynamic Linking
swift
1// Package.swift2let package = Package(3 name: "MyApp",4 products: [5 // Static linking - daha hızlı launch6 .library(name: "Core", type: .static, targets: ["Core"]),7 ],8 targets: [9 .target(name: "Core"),10 ]11)İlk Ekran Optimizasyonu
Skeleton Loading
swift
1struct HomeView: View {2 @State private var isLoading = true3 @State private var data: HomeData?4 5 var body: some View {6 Group {7 if isLoading {8 HomeSkeletonView()9 } else if let data = data {10 HomeContentView(data: data)11 }12 }13 .task {14 // Kritik data'yı önce yükle15 data = await loadCriticalData()16 isLoading = false17 18 // Diğer data'ları background'da19 Task.detached(priority: .background) {20 await preloadSecondaryData()21 }22 }23 }24}25 26struct HomeSkeletonView: View {27 var body: some View {28 VStack(spacing: 16) {29 // Header skeleton30 RoundedRectangle(cornerRadius: 8)31 .fill(.gray.opacity(0.3))32 .frame(height: 60)33 .shimmer()34 35 // Content skeletons36 ForEach(0..<5) { _ in37 RoundedRectangle(cornerRadius: 12)38 .fill(.gray.opacity(0.3))39 .frame(height: 100)40 .shimmer()41 }42 }43 .padding()44 }45}46 47// Shimmer effect48struct ShimmerModifier: ViewModifier {49 @State private var phase: CGFloat = 050 51 func body(content: Content) -> some View {52 content53 .overlay(54 LinearGradient(55 colors: [.clear, .white.opacity(0.5), .clear],56 startPoint: .leading,57 endPoint: .trailing58 )59 .offset(x: phase)60 )61 .mask(content)62 .onAppear {63 withAnimation(.linear(duration: 1.5).repeatForever(autoreverses: false)) {64 phase = 40065 }66 }67 }68}Instruments ile Profiling
App Launch Template
- Xcode -> Product -> Profile (⌘I)
- "App Launch" template seçin
- Record butonuna tıklayın
- Uygulama açılışını analiz edin
Önemli Metrikler
Metrik | İyi | Kabul Edilebilir | Kötü |
|---|---|---|---|
Cold launch | <1s | 1-2s | >2s |
Warm launch | <0.5s | 0.5-1s | >1s |
Hot launch | <0.2s | 0.2-0.5s | >0.5s |
MetricKit ile Monitoring
swift
1import MetricKit2 3class LaunchMetricsManager: NSObject, MXMetricManagerSubscriber {4 static let shared = LaunchMetricsManager()5 6 func startCollecting() {7 MXMetricManager.shared.add(self)8 }9 10 func didReceive(_ payloads: [MXMetricPayload]) {11 for payload in payloads {12 if let launchMetrics = payload.applicationLaunchMetrics {13 let histogram = launchMetrics.histogrammedTimeToFirstDraw14 15 // Analytics'e gönder16 Analytics.log(17 event: "launch_time",18 params: [19 "p50": histogram.bucketEnumerator.allObjects[50],20 "p95": histogram.bucketEnumerator.allObjects[95],21 "p99": histogram.bucketEnumerator.allObjects[99]22 ]23 )24 }25 }26 }27}Checklist
- +load metodlarını kaldırın
- Lazy initialization kullanın
- Framework sayısını minimize edin
- Static linking tercih edin
- İlk ekranı optimize edin (skeleton)
- Async service initialization
- MetricKit ile monitoring
- Cold/warm/hot launch testleri
Launch Time Regression Tespiti
Production'da launch time regresyonlarini tespit etmek, optimizasyon kadar onemlidir. CI/CD pipeline'iniza entegre edebileceginiz bir monitoring sistemi kurmaniz gerekir.
os_signpost ile Custom Olcum
swift
1import os.signpost2 3// Launch time olcum icin dedicated log4private let launchLog = OSLog(subsystem: "com.myapp", category: "Launch")5 6@main7struct MyApp: App {8 init() {9 // Launch baslangicindan itibaren olcumu baslat10 os_signpost(.begin, log: launchLog, name: "AppLaunch")11 }12 13 var body: some Scene {14 WindowGroup {15 ContentView()16 .onAppear {17 // Ilk frame renderlandiginda olcumu bitir18 os_signpost(.end, log: launchLog, name: "AppLaunch")19 reportLaunchTime()20 }21 }22 }23 24 private func reportLaunchTime() {25 let launchTime = CFAbsoluteTimeGetCurrent() - processStartTime26 27 // Backend'e gonder28 Analytics.shared.track(.launchTime(29 duration: launchTime,30 type: isWarmLaunch ? .warm : .cold,31 device: UIDevice.current.model,32 osVersion: UIDevice.current.systemVersion33 ))34 }35}XCTest ile Otomatik Launch Time Testi
swift
1import XCTest2 3final class LaunchPerformanceTests: XCTestCase {4 func testColdLaunchTime() throws {5 // Cold launch performance testi6 measure(metrics: [XCTApplicationLaunchMetric()]) {7 XCUIApplication().launch()8 }9 }10 11 func testColdLaunchUnder2Seconds() throws {12 let app = XCUIApplication()13 let startTime = CFAbsoluteTimeGetCurrent()14 app.launch()15 let elapsed = CFAbsoluteTimeGetCurrent() - startTime16 17 XCTAssertLessThan(elapsed, 2.0,18 "Cold launch 2 saniyenin altinda olmali, simdiki: \(elapsed)s")19 }20}Launch Time Metrikleri Dashboard
Metrik | Hedef | Uyari | Kritik |
|---|---|---|---|
Cold Launch (p50) | <800ms | 800ms-1.5s | >1.5s |
Cold Launch (p95) | <1.5s | 1.5s-2.5s | >2.5s |
Warm Launch (p50) | <400ms | 400ms-800ms | >800ms |
Pre-main Time | <200ms | 200ms-500ms | >500ms |
Time to First Frame | <500ms | 500ms-1s | >1s |
Dylib Loading | <100ms | 100ms-300ms | >300ms |
Bu metrikleri haftalik olarak izleyerek, herhangi bir regresyonu erken asamada yakalayabilirsiniz. Ozellikle buyuk feature branch'lerin merge'inden sonra launch time testlerini mutlaka calistirin.
Sonuç
App launch optimizasyonu, küçük değişikliklerin büyük etkiler yarattığı bir alandır. Pre-main ve post-main fazlarını anlayarak, lazy initialization kullanarak ve gereksiz framework'leri kaldırarak launch time'ı %70'e kadar iyileştirebilirsiniz.
Easter Egg
Gizli bir bilgi buldun!
Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?
Okuyucu Ödülü
Tebrikler! Bu yazıyı sonuna kadar okuduğun için sana özel bir hediyem var:
ALTIN İPUCU
Bu yazının en değerli bilgisi
Bu ipucu, yazının en önemli çıkarımını içeriyor.

