- Published on
Hammasini bir joyga qo'yish: refaktor mashqi
- Authors
- Name
- ShoxruxC
- @iOSdasturchi
Arxitektura patternlarini tushunishning eng yaxshi usuli — ularni bir xil ilovaga bosqichma-bosqich tatbiq etishni ko'rish. Bu darsda chalkash bitta faylni olib, har bir patternni qatlama-qatlam qo'llaysiz.
Ilova oddiy kitob o'qish kuzatuvchi. Har bir qadamda ilovani ishga tushiring — funksionalligi o'zgarmasligi kerak, faqat tuzilma.
1-qadam: boshlang'ich holat — hamma narsa bitta faylda
// ContentView.swift — oldingi holat
import SwiftUI
struct Kitob: Identifiable {
let id = UUID()
var sarlavha: String
var o'qildi: Bool
}
struct ContentView: View {
@State private var kitoblar: [Kitob] = [
Kitob(sarlavha: "Swift dasturlash tili", o'qildi: true),
Kitob(sarlavha: "Toza kod", o'qildi: false),
]
@State private var yangiSarlavha = ""
@State private var xatoXabar: String? = nil
@State private var faqatO'qilmaganlar = false
var filterlangan: [Kitob] {
faqatO'qilmaganlar ? kitoblar.filter { !$0.o'qildi } : kitoblar
}
var body: some View {
NavigationStack {
VStack {
Toggle("Faqat o'qilmaganlar", isOn: $faqatO'qilmaganlar).padding()
HStack {
TextField("Kitob sarlavhasi", text: $yangiSarlavha)
Button("Qo'shish") {
let tozalangan = yangiSarlavha.trimmingCharacters(in: .whitespaces)
guard !tozalangan.isEmpty else {
xatoXabar = "Sarlavha bo'sh bo'lishi mumkin emas."
return
}
kitoblar.append(Kitob(sarlavha: tozalangan, o'qildi: false))
yangiSarlavha = ""
xatoXabar = nil
}
}.padding(.horizontal)
if let xato = xatoXabar { Text(xato).foregroundStyle(.red) }
List(filterlangan) { kitob in Text(kitob.sarlavha) }
}
.navigationTitle("Kitob kuzatuvchi")
}
}
}
2-qadam: modelni alohida faylga ko'chirish
// Kitob.swift — Shared/Models/ papkasida yangi fayl
import Foundation
struct Kitob: Identifiable {
let id = UUID()
var sarlavha: String
var o'qildi: Bool
}
// ContentView.swift dan Kitob ta'rifini o'chirib tashlang
3-qadam: repository qo'shish
// KitobRepository.swift — Features/Books/ da
import Foundation
protocol KitobRepository {
func kitoblarniYukla() -> [Kitob]
mutating func kitobQosh(sarlavha: String)
}
// Haqiqiy implementatsiya — hozircha xotira massivi
struct XotiradagiKitobRepository: KitobRepository {
private var kitoblar: [Kitob] = [
Kitob(sarlavha: "Swift dasturlash tili", o'qildi: true),
Kitob(sarlavha: "Toza kod", o'qildi: false),
]
func kitoblarniYukla() -> [Kitob] { kitoblar }
mutating func kitobQosh(sarlavha: String) {
kitoblar.append(Kitob(sarlavha: sarlavha, o'qildi: false))
}
}
4-qadam: view model qo'shish
// KitobKorinishiModeli.swift — Features/Books/ da
import SwiftUI
@Observable
class KitobKorinishiModeli {
var kitoblar: [Kitob] = []
var xatoXabar: String? = nil
var faqatO'qilmaganlar = false
// Filtrlash — ma'lumot almashtirish, viewga emas bu yerga tegishli
var filterlanganKitoblar: [Kitob] {
faqatO'qilmaganlar ? kitoblar.filter { !$0.o'qildi } : kitoblar
}
private var repository: any KitobRepository
init(repository: any KitobRepository = XotiradagiKitobRepository()) {
self.repository = repository
self.kitoblar = repository.kitoblarniYukla()
}
func kitobQosh(_ sarlavha: String) {
let tozalangan = sarlavha.trimmingCharacters(in: .whitespaces)
guard !tozalangan.isEmpty else {
xatoXabar = "Sarlavha bo'sh bo'lishi mumkin emas."
return
}
repository.kitobQosh(sarlavha: tozalangan)
kitoblar = repository.kitoblarniYukla()
xatoXabar = nil
}
}
5-qadam: viewni tozalash
// ContentView.swift — keyin holat
// Hammasi shu yerda — view faqat UI ni boshqaradi
import SwiftUI
struct ContentView: View {
@State private var model = KitobKorinishiModeli()
@State private var yangiSarlavha = ""
var body: some View {
NavigationStack {
VStack {
Toggle("Faqat o'qilmaganlar", isOn: $model.faqatO'qilmaganlar).padding()
HStack {
TextField("Kitob sarlavhasi", text: $yangiSarlavha)
Button("Qo'shish") {
model.kitobQosh(yangiSarlavha)
yangiSarlavha = ""
}
}.padding(.horizontal)
if let xato = model.xatoXabar { Text(xato).foregroundStyle(.red) }
List(model.filterlanganKitoblar) { kitob in Text(kitob.sarlavha) }
}
.navigationTitle("Kitob kuzatuvchi")
}
}
}
// Preview — mock repository bilan
#Preview {
ContentView()
}
Natija — fayl tuzilmasi
KitobKuzatuvchi/
├── App/
│ └── KitobKuzatuvchiApp.swift
├── Features/
│ └── Books/
│ ├── ContentView.swift ← Faqat UI
│ ├── KitobKorinishiModeli.swift ← Biznes logikasi
│ └── KitobRepository.swift ← Ma'lumot kirishi
└── Shared/
└── Models/
└── Kitob.swift ← Model
Oldin: 1 fayl, 4 mas'uliyat
Keyin: 4 fayl, har biri 1 mas'uliyat
Nima o'zgardi
| Oldin | Keyin |
|---|---|
| Validatsiya viewda | kitobQosh() da |
| Filtrlash viewda computed property | filterlanganKitoblar view modelda |
Ma'lumot viewda @State massiv | Repository va view modelda |
| Test yozib bo'lmaydi | View modelini mustaqil sinash mumkin |
🎯 Topshiriq: o'z ilovangizni refaktor qiling
O'zingiz qurayotgan ilovani 5 qadamda refaktor qiling: Model → Repository → View modeli → Ko'rinish → Papka tuzilmasi. Har bir qadamdan keyin simulator da ishlatib tasdiqlang. Oxirigacha olganda kodni boshqa odamga berishga tayyor bo'ling — hech bir fayl nima qilayotganini tushuntirishga muhtoj bo'lmasdan.