- Published on
SwiftUI-da Core Data va MVVM arxitekturasi
- Authors
- Name
- ShoxruxC
- @iOSdasturchi
Avvalgi videoda Core Data-ni @FetchRequest orqali to'g'ridan-to'g'ri View ichida ishlatdik. Bu boshlang'ich uchun qulay, lekin production ilovalar uchun tavsiya etilmaydi — View ichida juda ko'p logika to'planib qoladi. Bu videoda xuddi shu Core Data logikasi MVVM arxitekturasi bilan qayta yoziladi: barcha logika ViewModel-ga ko'chiriladi, View faqat UI bilan shug'ullanadi.
@FetchRequest vs MVVM
Avvalgi video (@FetchRequest):
View = UI + @FetchRequest + addItem + deleteItem + saveItems
Bu video (MVVM):
View = faqat UI
CoreDataViewModel = NSPersistentContainer + fetchFruits + addFruit + deleteFruit + updateFruit + saveData
1-qadam: FruitsContainer yaratish
Navigator → yangi fayl → Core Data bo'limida Data Model → nom: FruitsContainer
Yaratilgan faylda:
- Add Entity →
FruitEntity - Add Attribute →
name, turiString
Container nomi (FruitsContainer) keyinchalik NSPersistentContainer-da ishlatiladi — aynan bir xil yozilishi shart.
2-qadam: CoreDataViewModel
import CoreData
class CoreDataViewModel: ObservableObject {
let container: NSPersistentContainer
@Published var savedEntities: [FruitEntity] = []
init() {
// Container nomi = FruitsContainer.xcdatamodeld fayl nomi
container = NSPersistentContainer(name: "FruitsContainer")
container.loadPersistentStores { _, error in
if let error = error {
print("Core Data yuklanmadi: \(error)")
} else {
print("Core Data muvaffaqiyatli yuklandi")
}
}
fetchFruits()
}
// MARK: - Fetch
func fetchFruits() {
// @FetchRequest o'rniga — ViewModel ichida NSFetchRequest
let request = NSFetchRequest<FruitEntity>(entityName: "FruitEntity")
// entityName = .xcdatamodeld dagi entity nomi bilan aynan bir xil
do {
savedEntities = try container.viewContext.fetch(request)
} catch {
print("Fetch xatosi: \(error)")
}
}
// MARK: - Add
func addFruit(text: String) {
let newFruit = FruitEntity(context: container.viewContext)
newFruit.name = text
saveData()
}
// MARK: - Delete
func deleteFruit(indexSet: IndexSet) {
guard let index = indexSet.first else { return }
let entity = savedEntities[index]
container.viewContext.delete(entity)
saveData()
}
// MARK: - Update
func updateFruit(entity: FruitEntity) {
let currentName = entity.name ?? ""
let newName = currentName + "!"
entity.name = newName
saveData()
}
// MARK: - Save
func saveData() {
do {
try container.viewContext.save()
// Saqlangandan keyin yangi ma'lumotlarni qayta yuklash
// @FetchRequest-dan farqi: bu qo'lda bajarilishi kerak
fetchFruits()
} catch {
print("Saqlash xatosi: \(error)")
}
}
}
Muhim: @FetchRequest ishlatilganda ma'lumot saqlanganda view avtomatik yangilanardi. MVVM-da esa saveData() ichida fetchFruits() qo'lda chaqirilishi kerak — aks holda savedEntities yangilanmaydi va ekran o'zgarmaydi.
3-qadam: View — faqat UI
struct CoreDataBootcamp: View {
@StateObject var vm: CoreDataViewModel = CoreDataViewModel()
@State private var textFieldText: String = ""
var body: some View {
NavigationView {
VStack(spacing: 20) {
// Kiritish maydoni
TextField("Meva nomini kiriting...", text: $textFieldText)
.font(.headline)
.padding(.leading)
.frame(height: 55)
.frame(maxWidth: .infinity)
.background(Color(.systemGray5))
.cornerRadius(10)
.padding(.horizontal)
// Qo'shish tugmasi
Button {
guard !textFieldText.isEmpty else { return }
vm.addFruit(text: textFieldText)
textFieldText = ""
} label: {
Text("Saqlash")
.font(.headline)
.foregroundColor(.white)
.frame(height: 55)
.frame(maxWidth: .infinity)
.background(Color.pink)
.cornerRadius(10)
}
.padding(.horizontal)
// Mevalar ro'yxati
List {
ForEach(vm.savedEntities) { entity in
Text(entity.name ?? "Nomsiz")
.onTapGesture {
vm.updateFruit(entity: entity)
}
}
.onDelete(perform: vm.deleteFruit)
}
.listStyle(PlainListStyle())
}
.navigationTitle("Mevalar")
}
}
}
View-da hech qanday Core Data logikasi yo'q — faqat vm.addFruit, vm.deleteFruit, vm.updateFruit chaqiriladi.
NSFetchRequest nozikliklari
Entity nomi
// ✅ TO'G'RI — .xcdatamodeld dagi entity nomi bilan aynan mos
let request = NSFetchRequest<FruitEntity>(entityName: "FruitEntity")
// ❌ XATO — katta-kichik harf farqiga e'tibor bering
let request = NSFetchRequest<FruitEntity>(entityName: "fruitEntity")
let request = NSFetchRequest<FruitEntity>(entityName: "Fruit_Entity")
Agar NSPersistentContainer nomi yoki NSFetchRequest-dagi entity nomi .xcdatamodeld fayli va entity nomidan farq qilsa — Core Data yuklanmaydi. Xato "error loading core data" xabari sifatida ko'rinadi.
Xcode qayta ishga tushirish
Yangi entity yaratilganda Xcode ba'zan uni darhol taniy olmaydi — "cannot find FruitEntity in scope" xatosi beradi. Bu holda Xcode-ni yopib qayta ochish (Command+Q, keyin qayta ochish) muammoni hal qiladi.
Saralash qo'shish
func fetchFruits() {
let request = NSFetchRequest<FruitEntity>(entityName: "FruitEntity")
// A → Z tartibda saralash
let sort = NSSortDescriptor(keyPath: \FruitEntity.name, ascending: true)
request.sortDescriptors = [sort]
do {
savedEntities = try container.viewContext.fetch(request)
} catch {
print("Fetch xatosi: \(error)")
}
}
@FetchRequest vs MVVM — qaysi yaxshiroq?
| @FetchRequest | MVVM | |
|---|---|---|
| Kod miqdori | Kam | Ko'proq |
| O'rganish qulayligi | Osonroq | Biroz murakkabroq |
| View da logika | Bor | Yo'q |
| Kengaytirish | Qiyin | Oson |
| Production uchun | Kam tavsiya | Tavsiya etiladi |
| Avtomatik yangilanish | Ha (@FetchRequest) | Qo'lda (fetchFruits qayta chaqiriladi) |
Kichik loyihalar uchun @FetchRequest yetarli. Katta va professional ilovalarda MVVM ancha samarali — View faqat UI bilan shug'ullanadi, ViewModel barcha data logikasini boshqaradi.
Xulosa — MVVM bilan Core Data asosiy qadamlari
1. .xcdatamodeld → entity va atributlar yaratish
2. CoreDataViewModel:
- NSPersistentContainer (container nomi = .xcdatamodeld fayl nomi)
- loadPersistentStores + fetchFruits (init ichida)
- @Published savedEntities: [FruitEntity]
- fetchFruits() — NSFetchRequest bilan
- addFruit(text:) — yangi entity + saveData()
- deleteFruit(indexSet:) — entity o'chirish + saveData()
- updateFruit(entity:) — entity yangilash + saveData()
- saveData() — viewContext.save() + fetchFruits()
3. View:
- @StateObject var vm = CoreDataViewModel()
- TextField + Button → vm.addFruit()
- List + ForEach(vm.savedEntities) → onDelete + onTapGesture
Core Data-dagi har bir o'zgarish (qo'shish, o'chirish, yangilash) uchun saveData() chaqirilishi shart — aks holda o'zgarishlar saqlana olmaydi.