Published on

SwiftUI da dependency injection

Authors

Repository patterningiz tayyor β€” lekin endi savol: view model repositoryni qayerdan oladi?

Dependency injection β€” turga uning kerakli narsalarini u o'zi yaratish o'rniga tashqaridan berish. Bu tushuncha oddiy, ammo u nima imkon beradi:

  • Ma'lumot manbasini bir joyda almashtirish
  • Preview va testlar uchun mock inject qilish
  • View modeli va repository bir-biridan mustaqil bo'lish

Constructor injection β€” eng oddiy usul

@Observable
class KitobKorinishiModeli {
    private let repository: any KitobRepository

    // Constructor injection β€” dependency yaratilish vaqtida uzatiladi
    // init() parametri orqali β€” oddiy va aniq
    init(repository: any KitobRepository = TarmoqKitobRepository()) {
        self.repository = repository
    }
}

// Produksiyada β€” hech narsa uzatmang, standart ishlatiladi
let model = KitobKorinishiModeli()

// Test yoki Preview da β€” mock uzating
let testModel = KitobKorinishiModeli(repository: MockKitobRepository())

Environment injection β€” keng miqyosli usul

Ko'p ko'rinish bitta repositoryga muhtoj bo'lsa, har birining konstruktoriga uzatish charchashga olib keladi. Environment injection bir marta inject qilish imkonini beradi.

// 1-qadam: Environment kalitini ro'yxatdan o'tkazish
struct KitobRepositoryKaliti: EnvironmentKey {
    // Ko'rinish environment da qiymat topilmasa shu standart ishlatiladi
    static let defaultValue: any KitobRepository = TarmoqKitobRepository()
}

// 2-qadam: EnvironmentValues ga nomlangan accessor qo'shish
extension EnvironmentValues {
    var kitobRepository: any KitobRepository {
        get { self[KitobRepositoryKaliti.self] }
        set { self[KitobRepositoryKaliti.self] = newValue }
    }
}

// 3-qadam: Ilova yoki sahna darajasida inject qilish
@main
struct MeningIlovam: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                // Bu qatorni o'zgartirish β€” butun ilovaning ma'lumot manbasini almashtiradi
                .environment(\.kitobRepository, TarmoqKitobRepository())
        }
    }
}

// 4-qadam: Ierarxiyadagi istalgan joyda o'qish
struct KitobRo'yxatiKorinishi: View {
    @Environment(\.kitobRepository) var repository
    @State private var model: KitobKorinishiModeli?

    var body: some View {
        Text("Kitoblar ro'yxati")
            .onAppear {
                model = KitobKorinishiModeli(repository: repository)
            }
    }
}
QatorVazifasi
struct KitobRepositoryKaliti: EnvironmentKeySwiftUI environment tizimida yangi kalit ro'yxatdan o'tkazadi. defaultValue β€” hech kim inject qilmasa ishlatiladi.
extension EnvironmentValues\.kitobRepository kabi nomlangan accessor qo'shadi.
.environment(\.kitobRepository, TarmoqKitobRepository())Bu nuqtadan pastdagi barcha viewlarga dependency inject qiladi.
@Environment(\.kitobRepository) var repositoryKo'rinishga environment dan dependency o'qiydi.

@Observable ob'ektlarni inject qilish β€” yanada oddiy

@Observable
class IlovaSozlamalari {
    var qorongRejimda = false
}

// Ilova kirish nuqtasida inject qilish β€” kalit kerak emas
ContentView()
    .environment(IlovaSozlamalari())

// Istalgan viewda o'qish β€” tur bilan
@Environment(IlovaSozlamalari.self) var sozlamalar

Preview da mock

#Preview {
    KitobRo'yxatiKorinishi()
        // Bitta modifier barcha pastdagi viewlarga mock beradi
        .environment(\.kitobRepository, MockKitobRepository())
}

Qaysi usulni tanlash

// Constructor injection β€” oddiy va aniq
// Foydalaning: view model va bitta yoki ikki ko'rinish uchun
init(repository: any KitobRepository = TarmoqKitobRepository()) {
    self.repository = repository
}

// Environment injection β€” keng miqyosli
// Foydalaning: ko'p viewda kerak, har biriga uzatmaslik uchun
.environment(\.kitobRepository, TarmoqKitobRepository())

Qoida: Constructor injection bilan boshlang β€” oddiy va aniq. Ko'p ko'rinishga kerakli dependency bo'lsa environment ga o'ting.

Tezkor ma'lumotnoma

SintaksisVazifasi
init(dep: any Protocol = HaqiqiyImpl())Constructor injection standart bilan
struct Kalit: EnvironmentKey { ... }Environment da yangi dependency ro'yxatdan o'tkazadi
extension EnvironmentValues { var dep }Nomlangan accessor β€” \.depNomi yozish imkoni
.environment(\.depNomi, implementatsiya)Ierarxiyadagi barcha pastki viewlarga inject qiladi
@Environment(\.depNomi) var depKo'rinishdan environment da o'qiydi
.environment(MeningObservable())@Observable ob'ektni tur bilan inject qiladi β€” kalit shart emas

🎯 Topshiriq: environment injection

10.3 darsdan VazifaRepository ni oling. Constructor injection ishlatayotgan bo'lsangiz β€” environment injection ga o'tkazib ko'ring. VazifaRepositoryKaliti va EnvironmentValues extension yozing. MyApp da LocalVazifaRepository inject qiling. Preview da MockVazifaRepository inject qiling. Ikkala holatda ham to'g'ri ma'lumot ko'rinishini tasdiqlang.

Buy mea coffee