# 02. Creational: Singleton — Haqiqat, Qotil va Yechim

> "Singleton — bu junior dasturchining eng sevimli quroli, chunki u har qanday ma'lumotni istalgan joydan olish imkonini beradi. Ammo aynan shu qurol 6 oydan keyin butun loyihani test qilib bo'lmaydigan, race conditionlarga to'la botqoqqa aylantiradi."

---

## 1. Singleton Nima va U Qanday Ishlaydi?

**Singleton** — bu dasturning butun ishlash davomida (Lifecycle) sinfning **faqat bitta nusxasi (instance)** mavjud bo'lishini va unga global kirish nuqtasi (`shared`) taqdim etilishini kafolatlovchi pattern.

### Klassik Swift Implementatsiyasi:
```swift
final class AnalyticsManager {
    // 1. Yagona global nusxa (Swift'da static let doimo thread-safe va lazy ishlaydi)
    static let shared = AnalyticsManager()
    
    // 2. Tashqaridan yangi nusxa ochishni TAQIQLASH shart!
    private init() {}
    
    func track(event: String) {
        print("Tadbir qayd etildi: \(event)")
    }
}
```

---

## 2. Apple Ekotizimidagi Singletonlar: "True" vs "Default"

Apple o'zining Cocoa va Foundation freymvorklarida Singleton patternidan keng foydalanadi:
* `UIApplication.shared` — haqiqiy (True) Singleton. Yangi `UIApplication()` ochish taqiqlangan.
* `FileManager.default` — Default Singleton. `FileManager.default` mavjud, biroq maxsus kesh papkalari uchun alohida `FileManager()` ochishingiz ham mumkin.
* `UserDefaults.standard` — Default Singleton.
* `URLSession.shared` — Default Singleton (o'z fon sessiyangiz uchun `URLSession(configuration:)` qura olasiz).

---

## 3. Singleton Nega "Anti-Pattern"ga Aylanadi? 4 Ta Halokatli Tuzoq

```mermaid
flowchart TD
    subgraph S1["Singletonning 4 Yashirin Qotili"]
        direction TB
        K1["1. Yashirin Bog'liqliklar (Hidden Dependencies)\nFunksiya nimaga bog'liqligi imzosida (Signature) ko'rinmaydi"]
        K2["2. Global Mutable State\nIstalgan View xohlagan paytda qiymatni o'zgartirib yuborishi mumkin"]
        K3["3. Unit Testlarning O'limi (Test Pollution)\n1-test o'zgartirgan state 2-test natijasini buzadi"]
        K4["4. Swift 6 Concurrency Race Conditions\nKo'p oqimli muhitda crash yoki data corruption"]
    end

    style K1 fill:#251014,stroke:#f87171,stroke-width:1px
    style K2 fill:#251014,stroke:#f87171,stroke-width:1px
    style K3 fill:#251014,stroke:#f87171,stroke-width:1px
    style K4 fill:#251014,stroke:#f87171,stroke-width:1px
```

### 1. Yashirin Bog'liqlik (Hidden Dependency) Fojiasi:
```swift
// ❌ YOMON: Funksiya tashqi dunyoga nima kerakligini yashirmoqda
func processOrder(order: Order) {
    // Bu funksiya AuthService, DatabaseManager va NetworkManager'ga
    // yashirin bog'langanini faqat ichki kodni o'qib bilish mumkin!
    let token = AuthService.shared.token
    DatabaseManager.shared.save(order)
    NetworkManager.shared.send(order, token: token)
}
```

### 2. Unit Test Falaji (State Pollution):
Tasavvur qiling, sizda `UserSession.shared.isLoggedIn` bor:
* **Test 1:** Login jarayonini tekshiradi va `isLoggedIn = true` qiladi.
* **Test 2:** Chiqish (Logout) jarayonini tekshirmoqchi. Lekin Test 1 dan qolgan `isLoggedIn = true` holati sababli Test 2 kutilmaganda fail bo'ladi!
* Singleton testlar o'rtasida xotiradan tozalanmaydi, testlarni alohida (isolated) yurgizib bo'lmaydi.

---

## 4. Swift 6 Concurrency da Singleton: Thread Safety

Swift 5 da ko'pchilik dasturchilar Singleton ichidagi o'zgaruvchilarni himoyasiz qoldirishar edi, bu ko'p oqimli (Multi-threaded) muhitda crash keltirib chiqarardi:

```mermaid
sequenceDiagram
    autonumber
    participant ThreadA as Thread A (Background)
    participant Singleton as SessionManager.shared
    participant ThreadB as Thread B (Main)

    ThreadA->>Singleton: session.token = "new_token" (Yozmoqda...)
    ThreadB->>Singleton: print(session.token) (O'qimoqda...)
    Singleton-->>Singleton: 💥 DATA RACE! Crash (EXC_BAD_ACCESS)!
```

### Swift 6 Yechimi: Global Actor Singleton
Swift 6 Strict Concurrency standartida Singleton'ni `actor` sifatida e'lon qilish ma'lumotlar poygasi (Data Race) ni kompilyatsiya bosqichidayoq to'liq bartaraf qiladi:

```swift
/// Swift 6 Concurrency uchun 100% xavfsiz Actor Singleton
public actor SecureSessionManager {
    public static let shared = SecureSessionManager()
    
    private var authToken: String?
    
    private init() {}
    
    public func updateToken(_ token: String) {
        self.authToken = token
    }
    
    public func getToken() -> String? {
        return self.authToken
    }
}
```

---

## 5. Senior Yechim: Singleton'ni Protocol va DI Bilan Zararsizlantirish

Singleton'dan butunlay voz kechish shart emas, uni **Dependency Injection (DI)** orqali boshqariladigan qilish kerak:

```mermaid
flowchart LR
    Protocol["protocol AnalyticsTracking (Kontrakt)"]
    Service["RealAnalyticsService.shared"] -.->|"Tasdiqlaydi"| Protocol
    Mock["MockAnalyticsService (Testlar uchun)"] -.->|"Tasdiqlaydi"| Protocol

    ViewModel["CheckoutViewModel"] -->|"faqat protocolga bog'langan"| Protocol

    style Protocol fill:#131720,stroke:#bef264,stroke-width:2px
    style ViewModel fill:#181d26,stroke:#7da2ce,stroke-width:1px
```

### Kod Misoli:
```swift
// 1. Protokol ajratish
public protocol AnalyticsTracking: Sendable {
    func track(event: String)
}

// 2. Haqiqiy Singleton (Protokolni tasdiqlaydi)
public final class ProductionAnalyticsService: AnalyticsTracking {
    public static let shared = ProductionAnalyticsService()
    private init() {}
    
    public func track(event: String) {
        // Firebase / Amplitude API chaqiruvi
    }
}

// 3. ViewModel: Singleton'ga to'g'ridan-to'g'ri bog'lanmaydi!
public final class CheckoutViewModel {
    private let analytics: any AnalyticsTracking
    
    // Default qiymat sifatida Singleton beriladi,
    // ammo testda istalgan Mock'ni uzatish mumkin!
    public init(analytics: any AnalyticsTracking = ProductionAnalyticsService.shared) {
        self.analytics = analytics
    }
    
    public func purchaseCompleted() {
        analytics.track(event: "purchase_success")
    }
}

// 4. Unit Test uchun Mock:
#if DEBUG
final class MockAnalyticsService: AnalyticsTracking {
    var trackedEvents: [String] = []
    func track(event: String) {
        trackedEvents.append(event)
    }
}
#endif
```

Endi `CheckoutViewModel` 100% testable bo'ldi! Test paytida haqiqiy Firebase'ga so'rov ketmaydi va xotira ifloslanmaydi.

---

## 6. Interaktiv Savol-Javob (Self-Check Quiz)

<details>
<summary><b>1-Savol: Nega Swift'da 'static let shared = MyClass()' avtomatik tarzda thread-safe hisoblanadi?</b> (Yechimni ko'rish uchun bosing)</summary>

**Tahlil va Yechim:**
Swift kompilyatori global va statik o'zgaruvchilarni ichki darajada `pthread_once` (yoki `dispatch_once`) orqali o'rab qo'yadi. Agar bir vaqtning o'zida 10 ta turli oqim (Thread) `MyClass.shared` ga murojaat qilsa ham, Swift faqat 1 ta birinchi oqimga obyektni xotirada yaratishga ruxsat beradi, qolgan 9 tasi esa bloklanib, tayyor obyektni qabul qiladi.
</details>

<details>
<summary><b>2-Savol: Qaysi holatda Singleton xotira oqishiga (Memory Leak / Retain Cycle) sabab bo'ladi?</b> (Yechimni ko'rish uchun bosing)</summary>

**Tahlil va Yechim:**
Singleton ilova yopilguncha xotirada (RAM) doimiy yashaydi. Agar siz Singleton ichidagi obunachilar (Observers yoki Delegates) massiviga `strong` havola bilan biror `UIViewController` yoki `View`ni saqlab qo'ysangiz, o'sha ViewController ekrandan yopilsa ham xotiradan o'chmaydi va butun umrga xotirada qolib ketadi (Retain Cycle). Yechim: Delegatlar va obunachilar doimo `weak` bo'lishi shart!
</details>

---

## 7. Amaliy Topshiriq (Senior Challenge)

Loyihangizdagi mavjud Singletonlardan birini (masalan `NetworkManager.shared`) oling:
1. Unga mos keluvchi `NetworkManaging` protokolini yozing.
2. Unga bog'langan ViewModel'ning `init` metodini Constructor Injection'ga o'tkazing (`init(network: any NetworkManaging = NetworkManager.shared)`).
3. Ushbu ViewModel uchun `MockNetworkManager` orqali muvaffaqiyatli ma'lumot kelishi va xatolik (404/500) kelishi holatlarini tekshiruvchi 2 ta Unit Test yozing!
