Azimjon Abdurasulov

08. Observer: KVO dan Combine va Swift 6 AsyncStream'gacha

🏛️ iOS Design Patterns — Mukammal Enterprise & Architecture Qo‘llanma

08. Behavioral: Observer Pattern — Reaktiv Dasturlash Asosi

"Agar siz ma'lumot yangilanganini bilish uchun har 1 soniyada taymer bilan tekshirib (Polling) tursangiz — bu batareyani o'ldirishdir. Observer patterni ma'lumot o'zgargandagina barcha qiziquvchilarni avtomatik uyg'otadi (Push Notification modeli)."


1. Observer Pattern Nima va Uning Apple Ekotizimidagi Evolyutsiyasi

Observer (Kuzatuvchi) — bu bitta obyektning (Subject/Publisher) holati o'zgarganda, unga obuna bo'lgan barcha boshqa obyektlarga (Observers/Subscribers) bu haqda avtomatik xabar beruvchi xulq-atvor andozasidir.

flowchart TD
    Subject["Subject (Masalan: CartManager / Savat)\nBalans yoki mahsulotlar o'zgardi!"]
    
    Subject -->|"notifyObservers()"| Obs1["Observer 1: CartBadgeView\n(Qizil doiradagi sonni yangilaydi)"]
    Subject -->|"notifyObservers()"| Obs2["Observer 2: CheckoutSummaryView\n(Jami summani qayta hisoblaydi)"]
    Subject -->|"notifyObservers()"| Obs3["Observer 3: AnalyticsTracker\n('cart_updated' eventini yuboradi)"]

    style Subject fill:#131720,stroke:#bef264,stroke-width:2px
    style Obs1 fill:#181d26,stroke:#7da2ce,stroke-width:1px
    style Obs2 fill:#181d26,stroke:#7da2ce,stroke-width:1px
    style Obs3 fill:#181d26,stroke:#7da2ce,stroke-width:1px

Apple Ekotizimida Observerning 4 Avlodi:

  1. 1-Avlod (Objective-C): NSNotificationCenter va KVO (Key-Value Observing). String kalitlar xavfli va har safar removeObserver() chaqirish esdan chiqsa crash bo'lardi.
  2. 2-Avlod (RxSwift): Uchinchi tomon reaktiv kutubxonalari.
  3. 3-Avlod (iOS 13+): Combine freymvorki (@Published, CurrentValueSubject, PassthroughSubject).
  4. 4-Avlod (Swift 5.9+ / Swift 6): Sof @Observable makrosi va AsyncSequence / AsyncStream.

2. Noldan O'zimizning Thread-Safe Observer Tizimimizni Qurish

Keling, hech qanday tashqi freymvorklarsiz sof Swift 6 da xotira oqishidan (Memory Leak) xoli Observer quramiz:

Retain Cycle Xavfi va Zaif Havola (Weak Wrapper):

Agar Subject kuzatuvchilarni oddiy massivda ([Observer]) saqlasa, u ularni kuchli (strong) ushlab turadi. Natijada obunachi ViewController yopilsa ham xotiradan o'chmay qoladi. Yechim: WeakObserverBox konteyneri:

import Foundation

// 1. Kuzatuvchi Protokoli
public protocol CartObserver: AnyObject, Sendable {
    func cartDidUpdate(totalItems: Int, totalPrice: Double)
}

// 2. Weak Wrapper (Xotira oqishini oldini oladi)
final class WeakObserverBox: @unchecked Sendable {
    weak var value: (any CartObserver)?
    init(_ value: any CartObserver) {
        self.value = value
    }
}

// 3. Markaziy Subject
public final class CartManager: @unchecked Sendable {
    public static let shared = CartManager()
    
    private let lock = NSLock()
    private var observers: [WeakObserverBox] = []
    
    private(set) var itemsCount: Int = 0
    private(set) var totalAmount: Double = 0.0
    
    private init() {}
    
    public func addObserver(_ observer: any CartObserver) {
        lock.lock()
        defer { lock.unlock() }
        // Avval o'lgan (nil bo'lgan) kuzatuvchilarni tozalaymiz
        observers.removeAll { $0.value == nil }
        observers.append(WeakObserverBox(observer))
    }
    
    public func addItem(price: Double) {
        lock.lock()
        itemsCount += 1
        totalAmount += price
        let currentCount = itemsCount
        let currentTotal = totalAmount
        // O'lik havolalarni tozalash
        observers.removeAll { $0.value == nil }
        let validObservers = observers.compactMap { $0.value }
        lock.unlock()
        
        // Barcha obunachilarni xabardor qilish
        for observer in validObservers {
            observer.cartDidUpdate(totalItems: currentCount, totalPrice: currentTotal)
        }
    }
}

3. Zamonaviy Swift 6 Standarti: @Observable va AsyncStream

Swift 6 da endi eski delegate yoki Combine o'rniga toza til xususiyatlari qo'llaniladi:

1. SwiftUI uchun @Observable:

import SwiftUI

@Observable
public final class StockTicker {
    public var applePrice: Double = 220.50
    public var googlePrice: Double = 175.20
    
    public func updatePrices(apple: Double, google: Double) {
        self.applePrice = apple
        self.googlePrice = google
    }
}

SwiftUI ko'rinishida faqat o'sha o'zgaruvchi ishlatilgan joylar avtomatik re-render bo'ladi!

2. Fon Oqimlari uchun AsyncStream:

public final class LocationTracker {
    public func locationStream() -> AsyncStream<String> {
        AsyncStream { continuation in
            // GPS yangilanganda
            continuation.yield("41.31, 69.24")
            
            continuation.onTermination = { _ in
                // GPS to'xtatildi
            }
        }
    }
}

// Qabul qiluvchi tomonda:
Task {
    for await coordinate in tracker.locationStream() {
        print("Yangi nuqta: \(coordinate)")
    }
}

4. Retain Cycle Qopqoni: [weak self] Nega Majburiy?

Combine yoki qadimiy NotificationCenter ishlatganda quyidagi xatoga yo'l qo'ymang:

// ❌ FOJIALI RETAIN CYCLE:
cancellable = cartService.$total
    .sink { newTotal in
        self.updateLabel(newTotal) // self kuchli ushlab qolindi!
    }

// ✅ TO'G'RI: [weak self]
cancellable = cartService.$total
    .sink { [weak self] newTotal in
        self?.updateLabel(newTotal)
    }

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

<details> <summary><b>1-Savol: Observer pattern va Pub/Sub (Publish-Subscribe) arxitekturasi o'rtasida qanday nozik farq bor?</b> (Yechimni ko'rish uchun bosing)</summary>

Tahlil va Yechim:

  • Observer patternida Subject obunachilar ro'yxatini to'g'ridan-to'g'ri o'zi biladi va ularning interfeys metodini chaqiradi.
  • Pub/Sub arxitekturasida (masalan, NotificationCenter yoki Redis Event Bus) Publisher va Subscriber bir-birini mutlaqo bilmaydi! Ularning o'rtasida mustaqil Message Broker / Event Channel turadi.

</details>

<details> <summary><b>2-Savol: Nega Swift 6 da '@Observable' makrosi Combine'dagi 'ObservableObject'dan 10x samaraliroq?</b> (Yechimni ko'rish uchun bosing)</summary>

Tahlil va Yechim: Combine'dagi ObservableObject da birorta @Published maydon o'zgarsa, objectWillChange orqali butun View qayta chizilardi (keraksiz renderlar). Yangi @Observable makrosi esa View ichida aynan qaysi bitta xususiyat o'qilgan bo'lsa (Fine-Grained Tracking), faqat o'sha yozuvnigina yangilaydi. </details>


6. Amaliy Topshiriq (Senior Challenge)

UserSessionSubject sinfini yarating:

  • Foydalanuvchi tizimga kirganida (login), chiqqanida (logout) va token yangilanganda barcha ro'yxatdan o'tgan kuzatuvchilarni xabardor qilsin.
  • Har bir obunachining xotiradan to'g'ri o'chirilishini (Weak Reference) Unit Test orqali tasdiqlang!

Markdown manba

iOS Lessons