- Published on
SwiftUI-da EnvironmentObject — global holda ma'lumot ulashish
- Authors
- Name
- ShoxruxC
- @iOSdasturchi
Bu video avvalgi — @StateObject va @ObservedObject videosining davomi. Agar u videoni ko'rmagan bo'lsangiz, avval uni ko'rib keliing.
Bu videoda biz @EnvironmentObject ni o'rganamiz. U @StateObject va @ObservedObject bilan bir xil ishlaydi, lekin katta farqi bor: ViewModel-ni ekrandan ekranga qo'lda uzatmasdan, muhitga qo'yib, barcha ekranlardan avtomatik olish mumkin.
Muammo — ViewModel-ni zanjir bo'ylab uzatish
Avvalgi videoda ViewModel-ni shunday uzatganmiz:
// 1-ekran — StateObject bilan yaratish
@StateObject var viewModel = EnvironmentViewModel()
// 2-ekranga uzatish
DetailView(viewModel: viewModel)
// 2-ekran — ObservedObject bilan qabul qilish
@ObservedObject var viewModel: EnvironmentViewModel
// 3-ekranga uzatish
FinalView(viewModel: viewModel)
// 3-ekran — ObservedObject bilan qabul qilish
@ObservedObject var viewModel: EnvironmentViewModel
Bu ishlaydi, lekin 5, 10, 20 ta ekran bo'lsa? Har bir ekranda @ObservedObject yozib, init-da qabul qilish juda charchattiradi. Bundan tashqari, 2-ekran ViewModel-ni faqat 3-ekranga uzatish uchun oladi — o'zi ishlatmaydi!
ViewModel yaratish
class EnvironmentViewModel: ObservableObject {
@Published var dataArray: [String] = []
init() {
getData()
}
func getData() {
self.dataArray.append(contentsOf: [
"iPhone",
"iPad",
"iMac",
"Apple Watch",
])
}
}
1-usul: @StateObject va @ObservedObject (avvalgi video)
// 1-ekran
struct EnvironmentObjectBootcamp: View {
@StateObject var viewModel = EnvironmentViewModel()
var body: some View {
NavigationView {
List {
ForEach(viewModel.dataArray, id: \.self) { item in
NavigationLink(item) {
// ViewModel-ni 2-ekranga uzatish shart
DetailView(selectedItem: item, viewModel: viewModel)
}
}
}
.navigationTitle("iOS qurilmalar")
}
}
}
// 2-ekran — ViewModel oladi, lekin ishlatmaydi
struct DetailView: View {
let selectedItem: String
@ObservedObject var viewModel: EnvironmentViewModel // faqat 3-ekranga uzatish uchun
var body: some View {
ZStack {
Color.orange.ignoresSafeArea()
NavigationLink {
// 3-ekranga uzatish shart
FinalView(viewModel: viewModel)
} label: {
Text(selectedItem)
.font(.headline)
.foregroundColor(.orange)
.padding()
.padding(.horizontal)
.background(Color.white.cornerRadius(30))
}
}
}
}
// 3-ekran
struct FinalView: View {
@ObservedObject var viewModel: EnvironmentViewModel
var body: some View {
ZStack {
LinearGradient(
colors: [Color.blue, Color.blue.opacity(0.7)],
startPoint: .topLeading,
endPoint: .bottomTrailing
).ignoresSafeArea()
ScrollView {
VStack(spacing: 20) {
ForEach(viewModel.dataArray, id: \.self) { item in
Text(item)
.font(.largeTitle)
.foregroundColor(.white)
}
}
}
}
}
}
Bu ishlaydi, lekin 2-ekran ViewModel-ni o'zi ishlatmaydi — faqat 3-ekranga uzatish uchun olgan.
2-usul: @EnvironmentObject (yaxshiroq yechim)
ViewModel-ni muhitga qo'yish — barcha ekranlar avtomatik oladi, uzatish shart emas:
// 1-ekran — ViewModel-ni muhitga qo'yish
struct EnvironmentObjectBootcamp: View {
@StateObject var viewModel = EnvironmentViewModel()
var body: some View {
NavigationView {
List {
ForEach(viewModel.dataArray, id: \.self) { item in
NavigationLink(item) {
// viewModel uzatilmaydi — muhitda mavjud
DetailView(selectedItem: item)
}
}
}
.navigationTitle("iOS qurilmalar")
}
// ViewModel-ni muhitga qo'yish
.environmentObject(viewModel)
}
}
// 2-ekran — ViewModel kerak emas, uzatmaydi
struct DetailView: View {
let selectedItem: String
// @ObservedObject yo'q — ViewModel-ni muhitdan olamiz
var body: some View {
ZStack {
Color.orange.ignoresSafeArea()
NavigationLink {
// viewModel uzatilmaydi
FinalView()
} label: {
Text(selectedItem)
.font(.headline)
.foregroundColor(.orange)
.padding()
.padding(.horizontal)
.background(Color.white.cornerRadius(30))
}
}
}
}
// 3-ekran — ViewModel-ni muhitdan olish
struct FinalView: View {
// @EnvironmentObject — muhitdan avtomatik olish
@EnvironmentObject var viewModel: EnvironmentViewModel
var body: some View {
ZStack {
LinearGradient(
colors: [Color.blue, Color.blue.opacity(0.7)],
startPoint: .topLeading,
endPoint: .bottomTrailing
).ignoresSafeArea()
ScrollView {
VStack(spacing: 20) {
ForEach(viewModel.dataArray, id: \.self) { item in
Text(item)
.font(.largeTitle)
.foregroundColor(.white)
}
}
}
}
}
}
Qanday ishlaydi?
1-ekran: .environmentObject(viewModel) ← muhitga qo'yildi
↓ NavigationLink
2-ekran: ViewModel kerak emas — muhitda bor, lekin ishlatilmaydi
Hech narsa uzatilmaydi
↓ NavigationLink
3-ekran: @EnvironmentObject var viewModel ← muhitdan olindi
Avtomatik — hech narsa uzatilmagan
ViewModel bir joyda yaratildi, bir joyda muhitga qo'yildi. Barcha pastki ekranlar undan xohlagan vaqtda olishi mumkin.
⚠️ Muhim ogohlantirish
// Agar muhitda EnvironmentViewModel bo'lmasa — ilova crash bo'ladi!
@EnvironmentObject var viewModel: EnvironmentViewModel
// Shuning uchun Preview-da ham muhitni sozlash kerak
struct FinalView_Previews: PreviewProvider {
static var previews: some View {
FinalView()
.environmentObject(EnvironmentViewModel()) // ← majburiy
}
}
@EnvironmentObject ishlatgan har bir View-ning Preview-ida .environmentObject(...) qo'shmasangiz, preview crash bo'ladi.
Uchta property wrapper taqqoslov
| Qayerda yaratiladi | Qanday olinadi | Qachon ishlatish | |
|---|---|---|---|
@StateObject | Birinchi View-da | — | ViewModel-ni birinchi marta yaratishda |
@ObservedObject | Boshqa View-dan uzatiladi | init parametri orqali | 1-2 ta ekranga uzatishda |
@EnvironmentObject | Muhitda (environmentObject) | Avtomatik | Ko'p ekranlarda bir ViewModel kerak bo'lganda |
Qachon EnvironmentObject ishlatish kerak?
// ✅ EnvironmentObject tavsiya etiladi:
// — 3+ ta ekran bir xil ViewModel kerak bo'lganda
// — Oraliq ekranlar ViewModel-ni ishlatmasa ham uzatishi kerak bo'lganda
// — Login holati, mavzu (dark/light) kabi global ma'lumotlar uchun
// ❌ EnvironmentObject ortiqcha bo'ladi:
// — Faqat 1-2 ta ekran bir ViewModel ishlatsa
// → bu holda @StateObject + @ObservedObject yaxshiroq
// — Juda ko'p EnvironmentObject qo'shish ilovani sekinlashtiradi
// va kodni chalkashtirib yuboradi
Xulosa
// Yaratish — bir marta, birinchi View-da
@StateObject var viewModel = EnvironmentViewModel()
// Muhitga qo'yish — barcha pastki ekranlar uchun
.environmentObject(viewModel)
// Olish — istalgan pastki ekranda, uzatmasdan
@EnvironmentObject var viewModel: EnvironmentViewModel
@EnvironmentObject — ilova bo'ylab ulashish kerak bo'lgan ma'lumotlar uchun ideal. Lekin ehtiyotkorlik bilan ishlating: faqat haqiqatan ham ko'p ekranlarda kerak bo'lgan ViewModel-larni muhitga qo'ying.
Rahmat, men Nick, bu Swiftful Thinking va keyingi videoda ko'rishguncha!