Published on

SwiftUI Map App — ViewModel: @StateObject va @EnvironmentObject

Authors

ViewModel: @StateObject va @EnvironmentObject

Bu videoda MVVM arxitekturasining markaziy qismi — LocationsViewModel quriladi va barcha view-larga @EnvironmentObject orqali uzatiladi.


LocationsView yaratish

Views/ papkasida yangi SwiftUI View fayli yaratiladi — LocationsView.swift. Bu ilovaning asosiy ekrani bo'ladi.


LocationsViewModel — boshlang'ich versiya

Avval ViewModel-ni xuddi shu faylda qurib ko'ramiz, keyin alohida faylga ko'chiramiz.

import Foundation
import MapKit

class LocationsViewModel: ObservableObject {

    @Published var locations: [Location] = []

    init() {
        let locations = LocationsDataService.locations
        self.locations = locations
    }
}

Va LocationsView-da:

struct LocationsView: View {

    @StateObject private var vm = LocationsViewModel()

    var body: some View {
        List {
            ForEach(vm.locations) { location in
                Text(location.name)
            }
        }
    }
}

Bu boshlang'ich versiya — @StateObject bilan faqat shu view ichida ishlatiladi. Keyinroq @EnvironmentObject-ga o'tkaziladi.


Location-ni Identifiable-ga moslashtirish

ForEach bilan ishlaganda Swift har bir elementni farqlash uchun noyob identifikator talab qiladi. Buning uchun Location struct-i Identifiable protokoliga mos bo'lishi kerak:

// ❌ Tez, lekin noto'g'ri
let id: String = UUID().uuidString
// Har safar yaratilganda yangi id hosil bo'ladi —
// ikkita bir xil Location turli id-ga ega bo'ladi

To'g'ri yondashuv — id-ni mavjud ma'lumotlardan hisoblash:

// ✅ To'g'ri
struct Location: Identifiable {
    let name: String
    let cityName: String
    // ... boshqa maydonlar

    var id: String {
        name + cityName
    }
}

Nima uchun name + cityName?

  • Bir shahardan ikki xil joy → nomlar farq qiladi → id farq qiladi ✅
  • Ikki shahardagi bir xil nomli joy → shahar nomi farq qiladi → id farq qiladi ✅
  • To'liq bir xil joy → id ham bir xil → Swift ularni bir ob'ekt deb biladi ✅

Haqiqiy ilovada bu id odatda backend ma'lumotlar bazasidagi unikal identifikator bo'ladi. Bu misolda ma'lumotlar bazasi yo'qligi uchun name + cityName kombinatsiyasi ishlatiladi.


@EnvironmentObject — ViewModel-ni butun ilovaga tarqatish

@StateObject bilan ViewModel faqat bitta view-da mavjud. Boshqa view-larga @Binding orqali uzatish kerak bo'ladi — bu katta loyihalarda chalkash bo'lib ketadi.

Yechim: ViewModel-ni ilova darajasida yaratib, @EnvironmentObject orqali barcha child view-larga tarqatish.

App.swift — ViewModel-ni muhitga qo'shish

@main
struct SwiftUIMapAppApp: App {

    @StateObject private var vm = LocationsViewModel()

    var body: some Scene {
        WindowGroup {
            LocationsView()
                .environmentObject(vm)
        }
    }
}

LocationsView — muhitdan olish

struct LocationsView: View {

    @EnvironmentObject private var vm: LocationsViewModel

    var body: some View {
        List {
            ForEach(vm.locations) { location in
                Text(location.name)
            }
        }
    }
}

Endi vm — yangi yaratilmaydi, muhitdan (environment) olinadi.

Preview uchun

Preview muhitda @EnvironmentObject mavjud bo'lmaydi — shuning uchun preview-ga alohida qo'shish kerak:

struct LocationsView_Previews: PreviewProvider {
    static var previews: some View {
        LocationsView()
            .environmentObject(LocationsViewModel())
    }
}

ViewModel-ni alohida faylga ko'chirish

LocationsViewModel kodi ViewModels/ papkasidagi yangi Swift FileLocationsViewModel.swift-ga ko'chiriladi:

import Foundation
import MapKit

class LocationsViewModel: ObservableObject {

    @Published var locations: [Location] = []

    init() {
        self.locations = LocationsDataService.locations
    }
}

@StateObject va @EnvironmentObject — taqqoslash

@StateObject@EnvironmentObject
Yaratilish joyiShu view ichidaApp yoki ota view-da
Kirish imkoniFaqat shu viewBarcha child view-lar
Uzatish usuli@Binding kerakAvtomatik, muhit orqali
Qachon ishlatiladiLokal holatGlobal, umumiy holat

Video oxiridagi loyiha holati

SwiftUIMapApp/
├── Models/
│   └── Location.swiftIdentifiable qo'shildi
├── Views/
│   └── LocationsView.swift     ← @EnvironmentObject ishlatadi
├── ViewModels/
│   └── LocationsViewModel.swift ← yangi fayl
├── DataServices/
│   └── LocationsDataService.swift
└── SwiftUIMapAppApp.swift      ← vm yaratiladi va muhitga uzatiladi

Ilovani build qilib LocationsView-da barcha joy nomlari ko'rinishi kerak — bu ViewModel to'g'ri ulanganligi belgisi.


Keyingi videoda LocationsView-ning asosiy UI qismi quriladi — xarita, pinlar va joy kartasi.

Buy mea coffee