- Published on
SwiftUI advanced (1-qism): custom property wrapper qanday yaratiladi?
- Authors
- Name
- ShoxruxC
- @iOSdasturchi
Property Wrapper nima?
SwiftUI dagi deyarli har bir ma'lumot qandaydir Property Wrapper orqali ishlaydi (@State, @Binding, @AppStorage, @Environment). Ammo ular "sehr" emas, balki aynan bizning kodimizga o'xshash, ma'lumotni saqlash va o'qish jarayonini boshqaruvchi oddiy kod (struct) hisoblanadi.
Bu darsda biz barcha kerakli funksiyalarga ega (FileManager'ga avtomatik saqlaydigan va undan o'qiydigan) o'zimizning maxsus Property Wrapper'imizni yaratamiz.
1. Dastlabki qadamlar (Muammo)
Tasavvur qiling, har safar o'zgaruvchi qiymati yangilanganda, uni FileManager'ga saqlash kerak bo'lsa:
@State private var title: String = "Hello"
Button("O'zgartirish") {
title = "Yangi matn"
saveToFileManager(title) // Har doim buni chaqirishni esdan chiqarmaslik kerak!
}
Buni unutilmasligi uchun barcha qutqarish (save) va o'qish (load) kodlarini bitta joyga yig'uvchi @FileManagerProperty degan o'zimizning Wrapper'ni yozamiz.
2. @propertyWrapper yaratish
Buning uchun biz Struct ochamiz va uni @propertyWrapper deb belgilaymiz. Shuningdek, SwiftUI ko'rinishni (View) yangilashi uchun DynamicProperty protokolidan foydalanamiz.
Har qanday Property Wrapper ichida wrappedValue nomli majburiy xususiyat bo'lishi shart!
import SwiftUI
@propertyWrapper
struct FileManagerProperty: DynamicProperty {
// Ichi o'zgarganda SwiftUI ga xabar berish uchun maxfiy State
@State private var title: String
let key: String
// O'zgaruvchining asosiy qiymati - wrappedValue deyiladi
var wrappedValue: String {
get {
return title
}
nonmutating set { // set paytida parent struct'ni mutating qilmaslik uchun nonmutating
title = newValue
save(newValue: newValue) // Avtomatik saqlash
}
}
// Initializer
init(wrappedValue: String, key: String) {
self.key = key
// Birinchi yaratilganda bazadan o'qiymiz
do {
let path = FileManager.documentsPath(key: key)
let savedString = try String(contentsOf: path, encoding: .utf8)
// State'ni maxsus ishga tushirish usuli:
self._title = State(wrappedValue: savedString)
} catch {
// Agar topilmasa default qiymat
self._title = State(wrappedValue: wrappedValue)
}
}
private func save(newValue: String) {
do {
let path = FileManager.documentsPath(key: key)
try newValue.write(to: path, atomically: false, encoding: .utf8)
print("Saqlandi!")
} catch {
print("Saqlashda xatolik: \(error)")
}
}
}
(Bu yerda FileManager qulayligi uchun biz oddiy documentsPath extension qildik deb tasavvur qilamiz).
3. $ (Dollar) belgisi qanday ishlaydi?
Agar biz yozgan Wrapper'ga $ orqali ham murojaat qila olishni (masalan Binding sifatida ishlatishni) xohlasak, unga projectedValue qo'shishimiz kerak. Odatda SwiftUI'ning @State yoki @AppStorage wrapper'lari o'zining projectedValue si sifatida Binding qaytaradi.
Kodimizga quyidagini qo'shamiz:
@propertyWrapper
struct FileManagerProperty: DynamicProperty {
// ... avvalgi kodlar ...
// Dollar ($) ishlatilganda nima qaytsin?
var projectedValue: Binding<String> {
Binding(
get: { wrappedValue },
set: { wrappedValue = $0 }
)
}
}
4. Foydalanish
Ana endi SwiftUI sahifamizda shunchaki yozamiz:
struct CustomWrapperView: View {
// Hech qanday murakkab kod yo'q!
@FileManagerProperty(key: "custom_title")
private var title: String = "Boshlang'ich Matn"
var body: some View {
VStack {
Text(title) // Oddiy o'qish
Button("O'zgartirish va saqlash") {
title = "Yangi nom" // Shunchaki o'zgartiramiz, avtomatik saqlanadi!
}
// TextField ga $ orqali Binding sifatida beramiz (projectedValue ishladi!)
TextField("Matn", text: $title)
}
}
}
Xulosa
Biz bitta kichik struct ichida qanchalik ko'p "sehr" borligini ko'rdik. wrappedValue - siz oddiy title deganda ko'rinadigan o'zgaruvchi. projectedValue - siz $title deganingizda qaytadigan o'zgaruvchi.
Custom Property Wrappers ilovangizdagi juda ko'p takrorlanuvchi ishlarni (baza bilan ishlash, tarmoqqa murojaat) soddalashtirish uchun mukammal vositadir.