# Dependency Injection (DI) va Zamonaviy DI Container'lar

Dasturlashda eng ko‘p uchraydigan va kodni mo‘rt (fragile), testlab bo‘lmaydigan qiluvchi xato — bu obyektlarning o‘z ichida qaramliklarni (dependencies) `init` qilishi (`tight coupling`). **Dependency Injection (DI)** — bu arxitekturaning fundamental tamoyili bo‘lib, Inversion of Control (IoC) va SOLID'ning Dependency Inversion Principle (DIP) qoidasini amalda ta’minlaydi.

Ushbu darsda biz DI shakllari, **Service Locator anti-pattern**i xavflari, Swift 6 da `Sendable` va actor-safe bo‘lgan yengil **DI Container** hamda `@Injected` Property Wrapper arxitekturasini quramiz.

---

## 1. Tight Coupling vs Inversion of Control (IoC)

Obyekt o‘ziga kerakli xizmatni o‘zi yaratsa:
```swift
// ❌ ANTI-PATTERN: Tight Coupling
final class ProfileViewModel {
    private let apiService = NetworkService() // Qattiq bog'langan!
    private let analytics = FirebaseAnalytics()
    // Testda NetworkService o'rniga Mock berib bo'lmaydi!
}
```

Buning o‘rniga, obyekt o‘z qaramliklarini tashqaridan qabul qilishi shart:

```mermaid
graph TD
    subgraph "Tight Coupling (O'tmish)"
        A[ProfileViewModel] -->|To'g'ridan-to'g'ri yaratadi| B[NetworkService]
        A -->|To'g'ridan-to'g'ri yaratadi| C[FirebaseAnalytics]
    end

    subgraph "Inversion of Control (DI)"
        VM[ProfileViewModel] -->|Faqat protokollarga bog'liq| P1[NetworkServiceProtocol]
        VM -->|Faqat protokollarga bog'liq| P2[AnalyticsServiceProtocol]
        DI[DI Container / Assembler] -.->|Injects Real Impl| P1
        DI -.->|Injects Mock Impl| P2
    end
```

---

## 2. Dependency Injection Shakllari

iOS'da DI ning 3 xil usuli mavjud:

| Usul | Afzalligi | Kamchiligi | Tavsiya |
| :--- | :--- | :--- | :--- |
| **Constructor (Initializer) Injection** | 100% Compile-time xavfsiz, immutability (`let`), qaramliksiz obyekt yaratilmaydi | Init parametrlar ko‘payib ketishi mumkin | ⭐ **Oltin standart (Default)** |
| **Property Injection** | Storyboard / UIKit lifecycle yoki ixtiyoriy pluginlar uchun qulay | Mutable (`var`) bo‘lishi shart, `nil` xavfi bor | Faqat shart bo‘lganda |
| **Method Injection** | Bir martalik parametr (masalan, formatter yoki logger) uzatish | Doimiy servislar uchun noqulay | Yordamchi amallarga |

### Constructor Injection Namunasi (Swift 6)

```swift
protocol NetworkServiceProtocol: Sendable {
    func fetchUserProfile(id: String) async throws -> UserProfile
}

protocol AnalyticsServiceProtocol: Sendable {
    func track(event: String)
}

@MainActor
final class ProfileViewModel {
    // Immutability: tashqaridan beriladi va qayta o'zgarmaydi
    private let networkService: any NetworkServiceProtocol
    private let analytics: any AnalyticsServiceProtocol
    
    // Constructor Injection
    init(
        networkService: any NetworkServiceProtocol,
        analytics: any AnalyticsServiceProtocol
    ) {
        self.networkService = networkService
        self.analytics = analytics
    }
    
    func loadUser(id: String) async {
        do {
            let profile = try await networkService.fetchUserProfile(id: id)
            analytics.track(event: "profile_loaded_success")
            // Update UI state
        } catch {
            analytics.track(event: "profile_load_failed")
        }
    }
}
```

---

## 3. Service Locator — Nega bu Anti-Pattern?

Ko‘plab jamoalar DI o‘rniga static `ServiceLocator` yaratishadi:

```swift
// ⚠️ XAVFLI: Service Locator Anti-Pattern
final class ServiceLocator {
    static let shared = ServiceLocator()
    private var services: [String: Any] = [:]
    
    func register<T>(_ service: T) {
        services[String(describing: T.self)] = service
    }
    
    func resolve<T>() -> T {
        guard let service = services[String(describing: T.self)] as? T else {
            fatalError("Servis topilmadi! Runtime Crash!")
        }
        return service
    }
}
```

### Nega Service Locator xavfli?
1. **Yashirin Qaramliklar (Hidden Dependencies)**: Sinf tashqaridan qaraganda hech qanday parametr so‘ramaydi (`init()`). Ammo ichida `ServiceLocator.shared.resolve()` chaqiriladi. Kodni o‘qigan dasturchi bu sinf ishlashi uchun qaysi servislar registratsiya qilingan bo‘lishi kerakligini bilmaydi.
2. **Runtime Crash**: Agar bitta servis unutilgan bo‘lsa, kod compile bo‘ladi, lekin dastur ishga tushganda `fatalError` beradi.
3. **Temporal Coupling**: Obyektlar yaratilish tartibiga qattiq bog‘lanib qoladi.

---

## 4. Swift 6 Xavfsiz, Yengil DI Container va `@Injected` Property Wrapper

Keling, zamonaviy SwiftUI va Swift Concurrency talablariga to‘liq javob beradigan, `Sendable` va type-safe **Lightweight DI Container** quramiz.

```mermaid
sequenceDiagram
    autonumber
    participant App as App Launch / Assembler
    participant Container as AppContainer (Actor)
    participant VM as HomeViewModel
    participant Network as LiveNetworkService

    App->>Container: register(LiveNetworkService.self)
    Note over Container: Servislar Type bo'yicha saqlanadi
    VM->>Container: resolve(NetworkServiceProtocol.self)
    Container-->>VM: return LiveNetworkService instance
    VM->>Network: fetchData()
```

### Swift 6 Real Implementatsiyasi:

```swift
import Foundation

// 1. Thread-safe DI Container
public final class DIContainer: @unchecked Sendable {
    public static let shared = DIContainer()
    
    private let lock = NSLock()
    private var factories: [String: () -> Any] = [:]
    private var singletons: [String: Any] = [:]
    
    public init() {}
    
    // Yangi factory registratsiyasi (Har safar yangi instance)
    public func register<T>(
        _ type: T.Type,
        factory: @escaping () -> T
    ) {
        lock.lock()
        defer { lock.unlock() }
        let key = String(reflecting: type)
        factories[key] = factory
    }
    
    // Singleton registratsiyasi (Bitta doimiy xotiradagi instance)
    public func registerSingleton<T>(
        _ type: T.Type,
        instance: T
    ) {
        lock.lock()
        defer { lock.unlock() }
        let key = String(reflecting: type)
        singletons[key] = instance
    }
    
    // Servisni qabul qilish
    public func resolve<T>(_ type: T.Type = T.self) -> T {
        lock.lock()
        defer { lock.unlock() }
        let key = String(reflecting: type)
        
        // 1. Singleton tekshiruvi
        if let instance = singletons[key] as? T {
            return instance
        }
        
        // 2. Factory tekshiruvi
        if let factory = factories[key], let instance = factory() as? T {
            return instance
        }
        
        fatalError("DIContainer: \(key) turi uchun servis registratsiya qilinmagan!")
    }
}

// 2. Ergonomik @Injected Property Wrapper
@propertyWrapper
public struct Injected<Service> {
    private var service: Service
    
    public init(container: DIContainer = .shared) {
        self.service = container.resolve(Service.self)
    }
    
    public var wrappedValue: Service {
        get { service }
        mutating set { service = newValue }
    }
}
```

---

## 5. SwiftUI va XCTest'da Foydalanish

### App Bootstrap:
```swift
func configureDependencies() {
    let container = DIContainer.shared
    
    container.registerSingleton(NetworkServiceProtocol.self, instance: LiveNetworkService())
    container.registerSingleton(AnalyticsServiceProtocol.self, instance: MixpanelAnalyticsService())
}
```

### SwiftUI ViewModel'da Ishlatish:
```swift
import SwiftUI

@Observable
@MainActor
final class HomeViewModel {
    @Injected private var network: NetworkServiceProtocol
    @Injected private var analytics: AnalyticsServiceProtocol
    
    var items: [String] = []
    
    func load() async {
        analytics.track(event: "home_screen_open")
        // Network orqali yuklash...
    }
}
```

### XCTest'da 100% Izolatsiyalangan Unit Test:
```swift
import XCTest

final class MockNetworkService: NetworkServiceProtocol {
    var stubbedProfile: UserProfile?
    var didCallFetch = false
    
    func fetchUserProfile(id: String) async throws -> UserProfile {
        didCallFetch = true
        if let profile = stubbedProfile {
            return profile
        }
        throw URLError(.badServerResponse)
    }
}

final class ProfileViewModelTests: XCTestCase {
    func test_loadUser_success() async {
        // DI Container'siz ham Constructor orqali to'g'ridan-to'g'ri mock berish eng toza yo'l:
        let mockNetwork = MockNetworkService()
        mockNetwork.stubbedProfile = UserProfile(id: "123", name: "Azimjon")
        let mockAnalytics = MockAnalyticsService()
        
        let sut = ProfileViewModel(networkService: mockNetwork, analytics: mockAnalytics)
        
        await sut.loadUser(id: "123")
        
        XCTAssertTrue(mockNetwork.didCallFetch)
        XCTAssertEqual(mockAnalytics.events, ["profile_loaded_success"])
    }
}
```

---

## 6. Senior & Staff Xulosa

1. **Over-engineering qilmang**: Agar ilovangiz o‘rtacha hajmda bo‘lsa, Constructor Injection + oddiy Factory / Coordinator barcha ehtiyojingizni qoplaydi. 10 xil uchinchi tomon DI kutubxonalarini (Swinject, Needle, Factory) yuklab loyihani og‘irlashtirish shart emas.
2. **SwiftUI Environment**: SwiftUI ning `@Environment` va `@EnvironmentObject` lari View darajasidagi Dependency Injection hisoblanadi. Ammo biznes-mantiq (ViewModel, Domain Interactor) darajasida Sof Swift DI usullari qo‘llanilishi maqsadga muvofiq.
3. **Property Wrapper ehtiyoti**: `@Injected` wrapper'laridan faqat arxitektura ruxsat bergan cheklangan qatlamlarda (masalan, View yoki Coordinator) foydalaning, toza biznes-mantiq sinflarida esa doimo **Constructor Injection**ni tanlang!

---

## Bilimni Mustahkamlash uchun Savollar

<details>
<summary><b>1. Nega Constructor Injection Property Injection'dan ustun hisoblanadi?</b></summary>
Constructor Injection ob’yektning barcha zaruriy qaramliklarini yaratilish paytidayoq (`init`) talab qiladi. Bu `let` (immutability) imkonini beradi va ob’yektning chala yoki yaroqsiz (`nil`) holatda yaratilishini compile vaqtidayoq taqiqlaydi.
</details>

<details>
<summary><b>2. Service Locator va Dependency Injection o'rtasidagi asosiy farq nima?</b></summary>
DI da ob’yekt o'z qaramliklaridan bexabar bo‘ladi — ular unga tashqaridan beriladi (passiv qabul qiluvchi). Service Locator'da esa ob’yekt global reyestrga murojaat qilib, o‘zi qidirib topadi (faol qidiruvchi). Bu esa qaramliklarni yashiradi va testlashni murakkablashtiradi.
</details>
