# iOS Performance Monitoring & Observability Rehberi
Uygulamaniz App Store'da yayinlandiktan sonra performans izleme kritik oneme sahiptir. Gelistirme ortamindaki testler gercek dunyadaki milyonlarca farkli cihaz, network kosulu ve kullanim senaryosunu temsil edemez. Bu rehberde Apple'in native araclari, ucuncu parti cozumler ve production-grade monitoring stratejilerini ele alacagiz.
Gercek: Kullanicilarin %53'u 3 saniyeden uzun suren sayfa yuklemelerinde uygulamayi terk ediyor (Google Research).
Icindekiler
- Observability Neden Onemli?
- MetricKit Framework
- OSLog ve Signpost
- Xcode Organizer
- Crash Reporting
- ANR Detection
- Network Monitoring
- Custom Telemetry
- Dashboard ve Alerting
- Best Practices
- Sonuc ve Oneriler
1. Observability Neden Onemli?
Observability uc temel sutun uzerine kurulur:
Sutun | Aciklama | iOS Araci |
|---|---|---|
**Metrics** | Sayisal olcumler | MetricKit, custom counters |
**Logs** | Olay kayitlari | OSLog, os_log |
**Traces** | Islem akis takibi | os_signpost, Instruments |
Performance Metrikleri ve Hedefler
Metrik | Hedef | Kritik Esik | Olcum Araci |
|---|---|---|---|
**App Launch** | < 400ms | > 2s | MetricKit |
**Hang Rate** | < %0.1 | > %1 | MetricKit |
**Memory Peak** | < 200MB | > 500MB | MetricKit |
**Crash Rate** | < %0.1 | > %1 | Xcode Organizer |
**Frame Drop** | < %5 | > %15 | CADisplayLink |
**Battery** | < %5/saat | > %10/saat | MetricKit |
**Disk Write** | < 100MB/gun | > 1GB/gun | MetricKit |
2. MetricKit Framework
MetricKit, Apple'in production performance olcum framework'udur. Gunluk ve anlik metrikler saglar.
swift
1import MetricKit2 3final class PerformanceMonitor: NSObject, MXMetricManagerSubscriber {4 static let shared = PerformanceMonitor()5 6 private override init() {7 super.init()8 }9 10 func startMonitoring() {11 let manager = MXMetricManager.shared12 manager.add(self)13 }14 15 func stopMonitoring() {16 MXMetricManager.shared.remove(self)17 }18 19 // Gunluk metrik raporu (24 saatte bir)20 func didReceive(_ payloads: [MXMetricPayload]) {21 for payload in payloads {22 processMetricPayload(payload)23 }24 }25 26 // Anlik teshis raporu (crash, hang vb.)27 func didReceive(_ payloads: [MXDiagnosticPayload]) {28 for payload in payloads {29 processDiagnosticPayload(payload)30 }31 }32 33 private func processMetricPayload(_ payload: MXMetricPayload) {34 // Launch metrikleri35 if let launchMetrics = payload.applicationLaunchMetrics {36 let resumeTime = launchMetrics.histogrammedTimeToFirstDraw37 logMetric("launch_time", histogram: resumeTime)38 }39 40 // Memory metrikleri41 if let memoryMetrics = payload.memoryMetrics {42 let peakMemory = memoryMetrics.peakMemoryUsage43 logMetric("peak_memory", measurement: peakMemory)44 }45 46 // CPU metrikleri47 if let cpuMetrics = payload.cpuMetrics {48 let cumulativeCPU = cpuMetrics.cumulativeCPUTime49 logMetric("cpu_time", measurement: cumulativeCPU)50 }51 52 // Disk I/O53 if let diskMetrics = payload.diskIOMetrics {54 let writes = diskMetrics.cumulativeLogicalWrites55 logMetric("disk_writes", measurement: writes)56 }57 58 // JSON olarak server'a gonder59 if let jsonData = payload.jsonRepresentation() {60 sendToServer(data: jsonData, type: "metric")61 }62 }63 64 private func processDiagnosticPayload(_ payload: MXDiagnosticPayload) {65 // Crash raporlari66 if let crashDiags = payload.crashDiagnostics {67 for crash in crashDiags {68 logDiagnostic("crash", callStack: crash.callStackTree)69 }70 }71 72 // Hang raporlari73 if let hangDiags = payload.hangDiagnostics {74 for hang in hangDiags {75 let duration = hang.hangDuration76 logDiagnostic("hang", duration: duration)77 }78 }79 80 // CPU exception81 if let cpuDiags = payload.cpuExceptionDiagnostics {82 for cpuException in cpuDiags {83 logDiagnostic("cpu_exception", callStack: cpuException.callStackTree)84 }85 }86 87 // Disk write exception88 if let diskDiags = payload.diskWriteExceptionDiagnostics {89 for diskException in diskDiags {90 logDiagnostic("disk_exception", callStack: diskException.callStackTree)91 }92 }93 94 if let jsonData = payload.jsonRepresentation() {95 sendToServer(data: jsonData, type: "diagnostic")96 }97 }98 99 private func logMetric(_ name: String, histogram: MXHistogram<UnitDuration>? = nil, measurement: Measurement<UnitDuration>? = nil) {100 // Analytics backend'e gonder101 }102 103 private func logDiagnostic(_ type: String, callStack: MXCallStackTree? = nil, duration: Measurement<UnitDuration>? = nil) {104 // Crash reporting backend'e gonder105 }106 107 private func sendToServer(data: Data, type: String) {108 // HTTP POST ile server'a gonder109 }110}Easter Egg
Gizli bir bilgi buldun!
Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?
3. OSLog ve Signpost
OSLog, Apple'in modern logging framework'udur. Console.app ve Instruments ile entegre calisir.
swift
1import OSLog2 3// Logger tanimlama4extension Logger {5 private static let subsystem = Bundle.main.bundleIdentifier ?? "com.app"6 7 static let network = Logger(subsystem: subsystem, category: "Network")8 static let database = Logger(subsystem: subsystem, category: "Database")9 static let ui = Logger(subsystem: subsystem, category: "UI")10 static let auth = Logger(subsystem: subsystem, category: "Auth")11 static let performance = Logger(subsystem: subsystem, category: "Performance")12}13 14// Kullanim15struct NetworkService {16 func fetchUsers() async throws -> [User] {17 Logger.network.info("Kullanici listesi isteniyor")18 19 let startTime = CFAbsoluteTimeGetCurrent()20 21 do {22 let users = try await apiClient.get("/users")23 let elapsed = CFAbsoluteTimeGetCurrent() - startTime24 25 Logger.network.info("Basarili: \(users.count) kullanici, \(elapsed, format: .fixed(precision: 2))s")26 Logger.performance.notice("API /users suresi: \(elapsed, format: .fixed(precision: 2))s")27 28 return users29 } catch {30 Logger.network.error("Hata: \(error.localizedDescription)")31 throw error32 }33 }34}OSLog Seviyeleri
Seviye | Kullanim | Disk'e Yazilir mi? | Performans Etkisi |
|---|---|---|---|
`.debug` | Gelistirme | Hayir | Yok |
`.info` | Genel bilgi | Hafizada, gerekirse | Minimal |
`.notice` | Onemli olaylar (default) | Evet | Dusuk |
`.error` | Hatalar | Evet | Dusuk |
`.fault` | Kritik hatalar | Evet + kalici | Dusuk |
4. Xcode Organizer
Xcode Organizer (Window > Organizer), App Store'daki uygulamanizin performans verilerini gosterir:
- Crashes: Crash raporlari ve stack trace'ler
- Energy: Pil tuketim raporlari
- Hangs: UI donma raporlari
- Disk Writes: Asiri disk yazimi
- Scrolling: Scroll performansi
- Launch Time: Uygulama acilis suresi
- Memory: Bellek kullanimi
5. Crash Reporting
swift
1// Custom crash handler2final class CrashReporter {3 static let shared = CrashReporter()4 5 func install() {6 // Uncaught exception handler7 NSSetUncaughtExceptionHandler { exception in8 let info = CrashInfo(9 name: exception.name.rawValue,10 reason: exception.reason ?? "Bilinmeyen",11 callStack: exception.callStackSymbols,12 timestamp: Date()13 )14 CrashReporter.shared.saveCrash(info)15 }16 17 // Signal handler (EXC_BAD_ACCESS vb.)18 setupSignalHandlers()19 20 // Onceki crash varsa gonder21 sendPendingCrashes()22 }23 24 private func saveCrash(_ info: CrashInfo) {25 // UserDefaults'a kaydet (FileManager crash sirasinda guvenli degil)26 if let data = try? JSONEncoder().encode(info) {27 UserDefaults.standard.set(data, forKey: "pending_crash")28 }29 }30 31 private func sendPendingCrashes() {32 guard let data = UserDefaults.standard.data(forKey: "pending_crash"),33 let crash = try? JSONDecoder().decode(CrashInfo.self, from: data) else {34 return35 }36 37 // Server'a gonder38 Task {39 do {40 try await uploadCrash(crash)41 UserDefaults.standard.removeObject(forKey: "pending_crash")42 } catch {43 Logger.performance.error("Crash raporu gonderilemedi")44 }45 }46 }47 48 private func setupSignalHandlers() {49 // SIGSEGV, SIGABRT, SIGBUS vb. icin50 }51 52 private func uploadCrash(_ crash: CrashInfo) async throws {53 // HTTP POST54 }55}56 57struct CrashInfo: Codable {58 let name: String59 let reason: String60 let callStack: [String]61 let timestamp: Date62}6. ANR (Application Not Responding) Detection
Main thread'in 250ms'den fazla bloklanmasi kullanici deneyimini bozar. Bunu tespit etmek icin watchdog mekanizmasi kurabilirsiniz.
swift
1final class HangDetector {2 private var watchdogThread: Thread?3 private let threshold: TimeInterval4 private var lastPing = Date()5 6 init(threshold: TimeInterval = 0.5) {7 self.threshold = threshold8 }9 10 func start() {11 // Main thread'den periyodik ping12 let mainTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in13 self?.lastPing = Date()14 }15 RunLoop.main.add(mainTimer, forMode: .common)16 17 // Background thread'den izleme18 watchdogThread = Thread {19 while !Thread.current.isCancelled {20 Thread.sleep(forTimeInterval: self.threshold)21 let elapsed = Date().timeIntervalSince(self.lastPing)22 if elapsed > self.threshold {23 self.reportHang(duration: elapsed)24 }25 }26 }27 watchdogThread?.name = "HangDetector"28 watchdogThread?.start()29 }30 31 func stop() {32 watchdogThread?.cancel()33 }34 35 private func reportHang(duration: TimeInterval) {36 let symbols = Thread.callStackSymbols37 Logger.performance.warning("Main thread hang: \(duration, format: .fixed(precision: 2))s")38 // Server'a bildir39 }40}7. Network Monitoring
URLSession metrikleri ile network performansini izleyin:
swift
1final class NetworkMetricsCollector: NSObject, URLSessionTaskDelegate {2 func urlSession(3 _ session: URLSession,4 task: URLSessionTask,5 didFinishCollecting metrics: URLSessionTaskMetrics6 ) {7 for transaction in metrics.transactionMetrics {8 let dns = transaction.domainLookupEndDate?.timeIntervalSince(9 transaction.domainLookupStartDate ?? Date()10 ) ?? 011 12 let connect = transaction.connectEndDate?.timeIntervalSince(13 transaction.connectStartDate ?? Date()14 ) ?? 015 16 let ttfb = transaction.responseStartDate?.timeIntervalSince(17 transaction.requestStartDate ?? Date()18 ) ?? 019 20 Logger.network.info(21 "DNS: \(dns)s, Connect: \(connect)s, TTFB: \(ttfb)s"22 )23 }24 }25}8. Custom Telemetry
Kendi metriklerinizi tanimlayin ve izleyin.
Telemetri Katmanlari
Katman | Icerik | Ornek |
|---|---|---|
**Teknik** | CPU, memory, disk | MetricKit |
**Uygulama** | API suresi, cache hit | Custom |
**Is** | Conversion, engagement | Analytics |
**Kullanici** | Session suresi, retention | Analytics |
9. Dashboard ve Alerting
Production monitoring icin bir dashboard kurulmalidir:
- Grafana + Prometheus: Acik kaynak, esnek
- Datadog: Kapsamli SaaS cozum
- Firebase Performance: Google'in ucretsiz araci
- Sentry: Hata ve performans izleme
- New Relic: Enterprise cozum
10. Best Practices
Monitoring Checklist
- MetricKit subscriber'i ekleyin
- OSLog ile yapisal logging yapin
- Crash reporting kurun
- Hang detection aktif edin
- Network metrikleri toplayin
- Launch time izleyin
- Memory uyarilari ekleyin
- Alerting kurallari tanimlayin
11. Sonuc ve Oneriler
Performance monitoring bir lüks degil, zorunluluktur. Kullanicilariniz sessizce terk etmeden once sorunlari tespit edin.
Oncelik Sirasi
- Crash reporting (en kritik)
- Launch time monitoring
- Hang detection
- Memory metrikleri
- Network performansi
- Custom is metrikleri
ALTIN İPUCU
Bu yazının en değerli bilgisi
Bu ipucu, yazının en önemli çıkarımını içeriyor.
Okuyucu Ödülü
Tebrikler! Bu yazıyı sonuna kadar okuduğun için sana özel bir hediyem var:

