Azimjon Abdurasulov

08. Performance, Caching va 120 FPS

⚡ Server-Driven UI (SDUI) — Mukammal Enterprise & System Design

08. Performance, Caching va 120 FPS ProMotion: Zero-Jank Arxitekturasi

"Foydalanuvchi ilovangiz SDUI ekanini bilmasligi kerak. Agar sahifani aylantirganda (scroll) mikro-qotish (jank) sezilsa yoki ekranni ochish 1 soniyadan ortiq vaqt olsa — arxitekturangiz yutqazdi."


1. Zero-Jank Qoidasi: ProMotion 8.33 ms Kadr Byudjeti

iPhone 13 Pro va undan keyingi barcha zamonaviy Apple qurilmalarida ekran yangilanish chastotasi 120 Hz (ProMotion) ni tashkil etadi. Bu har bir kadr ekranga chizilishi uchun protsessorga bor-yo'g'i 8.33 millisekund vaqt berilishini anglatadi:

$$\text{Frame Budget} = \frac{1000\text{ ms}}{120\text{ fps}} \approx 8.33\text{ ms}$$

Agar siz 60 KB lik SDUI JSON faylini Main Thread (asosiy oqim)da pars qilsangiz:

  • JSONDecoder().decode(...) o'rtacha 18–25 ms vaqt oladi.
  • Natijada 2-3 ta kadr tashlab ketiladi (Dropped Frames).
  • Foydalanuvchi barmoq bilan sahifani yurgizganda qotish (Jank/Stutter) yuz beradi!
flowchart TD
    subgraph BadThread["❌ Asosiy Oqimda (Main Thread) Parslash"]
        direction TB
        M1["Main Thread (UI + Touch + Render)"]
        M1 -->|"JSONDecoder.decode() ishga tushdi (22 ms)"| Frozen["⚠️ MAIN THREAD 22 ms GA MUZLADI!\n2 ta kadr yo'qoldi (Dropped Frames)\nFoydalanuvchi ekranning qotganini ko'radi!"]
    end

    subgraph GoodThread["✅ Swift 6 Background Actor Bilan"]
        direction TB
        Main["Main Thread: 120 FPS Silliq UI"]
        Actor["Background DataEngine Actor"]
        
        Main -.->|"URL request jo'natildi"| Actor
        Actor -->|"Fonda parslash va autorelease (20 ms)"| Actor
        Actor -->|"Tayyor [ComponentNode] yuborildi"| Main
        Main -->|"0.05 ms: Faqat View yangilandi"| Smooth["🚀 120 FPS Silliq Scrolling!"]
    end

    style BadThread fill:#251014,stroke:#f87171,stroke-width:1px
    style GoodThread fill:#0d1f14,stroke:#4ade80,stroke-width:1px

2. Swift 6 Background Actor bilan Parallallashtirish

JSON'ni tarmoqdan yuklab olish va Swift modellariga o'g'irish 100% fon oqimida (Background Actor) bajarilishi shart:

import Foundation

/// Swift 6 Concurrency asosidagi xavfsiz fon aktori
public actor SDUIDataEngine {
    public static let shared = SDUIDataEngine()
    private let decoder: JSONDecoder
    
    public init() {
        self.decoder = JSONDecoder()
    }
    
    /// To'liq background thread'da parslash
    public func parseScreen(from data: Data) throws -> SDUIScreenPayload {
        // Fon oqimida vaqtinchalik xotirani darhol tozalash uchun autoreleasepool
        try autoreleasepool {
            try decoder.decode(SDUIScreenPayload.self, from: data)
        }
    }
}

UI qatlamida esa faqat tayyor modellar @MainActor ga uzatiladi:

@MainActor
public func loadScreen(from url: URL) async {
    do {
        let (data, response) = try await URLSession.shared.data(from: url)
        
        // 1. Fon oqimida parslash (Main thread ozod!)
        let parsedScreen = try await SDUIDataEngine.shared.parseScreen(from: data)
        
        // 2. Asosiy oqimda faqat View'ni yangilash (0.05 ms)
        self.components = parsedScreen.components
    } catch {
        self.handleError(error)
    }
}

3. Stale-While-Revalidate (SWR) Kesh Strategiyasi

Foydalanuvchi metroda yoki internet sust joyda ilovani ochganda bo'sh oq ekran ko'rmasligi kerak:

sequenceDiagram
    autonumber
    participant User as Foydalanuvchi
    participant UI as SwiftUI View
    participant Cache as SDUICacheManager (Disk)
    participant Server as Backend API

    User->>UI: Ilovani ochadi
    UI->>Cache: Oxirgi snapshot bormi?
    Cache-->>UI: Ha! (data, eTag: "v2.4")
    UI-->>User: 0 ms: Ekranni darhol keshdan chizadi (Instant Launch!)
    
    UI->>Server: Orqa fonda: GET /screen (If-None-Match: "v2.4")
    alt Ma'lumot o'zgarmagan bo'lsa
        Server-->>UI: 304 Not Modified (0 bayt data!)
    else Yangi o'zgarish bo'lsa
        Server-->>UI: 200 OK + Yangi JSON
        UI->>Cache: Yangi snapshotni diskka yozish
        UI->>UI: Faqat o'zgargan qismlarni animatsiya bilan yangilash
        UI-->>User: Yangilangan UI ko'rinadi
    end

4. Swift 6 Kesh Boshqaruvchisi Kodi

import Foundation

public final class SDUICacheManager: Sendable {
    public static let shared = SDUICacheManager()
    private let fileManager = FileManager.default
    private let cacheDirectory: URL
    
    private init() {
        let paths = fileManager.urls(for: .cachesDirectory, in: .userDomainMask)
        self.cacheDirectory = paths[0].appendingPathComponent("SDUISnapshots")
        try? fileManager.createDirectory(at: cacheDirectory, withIntermediateDirectories: true)
    }
    
    public func saveSnapshot(data: Data, eTag: String?, for endpoint: String) {
        let fileURL = cacheDirectory.appendingPathComponent(endpoint.addingPercentEncoding(withAllowedCharacters: .alphanumerics) ?? "cache")
        try? data.write(to: fileURL)
        
        if let eTag = eTag {
            UserDefaults.standard.set(eTag, forKey: "etag_\(endpoint)")
        }
    }
    
    public func getCachedSnapshot(for endpoint: String) -> (data: Data, eTag: String?)? {
        let fileURL = cacheDirectory.appendingPathComponent(endpoint.addingPercentEncoding(withAllowedCharacters: .alphanumerics) ?? "cache")
        guard let data = try? Data(contentsOf: fileURL) else { return nil }
        let eTag = UserDefaults.standard.string(forKey: "etag_\(endpoint)")
        return (data, eTag)
    }
}

5. Interaktiv Savol-Javob (Self-Check Quiz)

<details> <summary><b>1-Savol: Nega parslash jarayonida 'autoreleasepool' blokini ishlatish xotira to'lib ketishining oldini oladi?</b> (Yechimni ko'rish uchun bosing)</summary>

Tahlil va Yechim: JSONDecoder parslash jarayonida millionlab kichik vaqtinchalik String, Substring va Dictionary obyektlarini yaratadi. Odatda bu obyektlar RunLoop tugaguncha xotirada qolib ketadi. autoreleasepool { ... } ishlatilganda, blok yakunlanishi bilanoq barcha vaqtinchalik obyektlar darhol RAM'dan o'chiriladi va xotira cho'qqisi (Memory Spike) sababli ilovaning iOS tomonidan o'ldirilishi (Jetsam OOM) oldi olinadi. </details>

<details> <summary><b>2-Savol: HTTP 304 Not Modified statusi SDUI tizimlariga qanday foyda keltiradi?</b> (Yechimni ko'rish uchun bosing)</summary>

Tahlil va Yechim: Mobil ilova so'rov yuborayotganda keshdagi ETag qiymatini If-None-Match sarlavhasida jo'natadi. Agar serverda ma'lumot o'zgarmagan bo'lsa, server bo'sh (0 baytlik) 304 javobini qaytaradi. Bu foydalanuvchi mobil trafigini tejaydi, batareya quvvatini sarflamaydi va serverdagi ma'lumotlar bazasi yuklamasini keskin kamaytiradi. </details>


6. Amaliy Topshiriq (Senior Challenge)

O'z loyihangizda SDUIDataEngine actorini yarating. 10 000 ta obyektdan iborat sintetik JSON ma'lumotini avval Main Threadda, so'ngra actor orqali fonda pars qilib ko'ring. CFAbsoluteTimeGetCurrent() orqali vaqtlarni solishtiring va konsolda natijani qayd eting.

Markdown manba

iOS Lessons