Published on

Swiftda Property Wrappers

Authors

Property Wrapper β€” property ga "xulq-atvor" qo'shish usuli. SwiftUI da har kuni ishlatiladigan @State, @Published, @AppStorage, @Binding β€” bularning hammasi property wrapper. O'zingiz ham yaratishingiz mumkin β€” qiymatni cheklash, tekshirish, avtomatik saqlash kabi logikalarni bitta joyda yozib, ko'p property larda qayta ishlatish.

Property Wrapper ning asosiy tushunchalari: wrappedValue β€” foydalanuvchi ko'radigan va o'zgartiradigan asosiy qiymat. projectedValue ($ prefiksi bilan) β€” qo'shimcha ma'lumot (masalan, @State da $name Binding qaytaradi).

Property Wrapper yaratish

@propertyWrapper atributi struct yoki class ni property wrapper sifatida belgilaydi. wrappedValue property majburiy. Quyida qiymatni belgilangan oraliqda cheklaydigan wrapper ko'rsatilgan.

// ═══════════════════════════════════════
//  @PROPERTYWRAPPER β€” o'z wrapper imizni yaratamiz
//  Bu wrapper qiymatni 0...100 oraliqda cheklaydi
// ═══════════════════════════════════════
@propertyWrapper
struct Chegaralangan {
    private var qiymat: Int
    private let min: Int
    private let max: Int

    // wrappedValue β€” asosiy qiymat (tashqaridan ko'rinadigan)
    var wrappedValue: Int {
        get { qiymat }
        set { qiymat = Swift.min(Swift.max(newValue, min), max) }
        // newValue min dan kichik bo'lsa min, max dan katta bo'lsa max
    }

    init(wrappedValue: Int, min: Int, max: Int) {
        self.min = min
        self.max = max
        // Init da ham cheklash
        self.qiymat = Swift.min(Swift.max(wrappedValue, min), max)
    }
}

// Ishlatish β€” @ bilan
struct OyinBotirma {
    @Chegaralangan(min: 0, max: 100) var hayot = 100
    @Chegaralangan(min: 0, max: 10) var daraja = 1
}

var botir = OyinBotirma()
botir.hayot = 150   // 100 ga chegaralanadi (max)
print(botir.hayot)  // 100

botir.hayot = -20   // 0 ga chegaralanadi (min)
print(botir.hayot)  // 0

botir.daraja = 15   // 10 ga chegaralanadi
print(botir.daraja) // 10

projectedValue β€” $ bilan qo'shimcha ma'lumot

projectedValue β€” $ prefiksi bilan chaqiriladigan qo'shimcha qiymat. Masalan, @State var ism = "" da $ism Binding<String> qaytaradi. O'z wrapper ingizda projectedValue ni qo'shib, qo'shimcha ma'lumot berish mumkin.

// ═══════════════════════════════════════
//  PROJECTEDVALUE β€” $ prefiksi bilan chaqiriladigan qo'shimcha qiymat
// ═══════════════════════════════════════
@propertyWrapper
struct BoshHarfli {
    private var matn: String

    var wrappedValue: String {
        get { matn }
        set { matn = newValue.capitalized }
        // .capitalized β€” har so'zning bosh harfi katta
    }

    // $ bilan chaqiriladi β€” asl qiymatni ko'rsatadi
    var projectedValue: String {
        matn.uppercased()  // HAMMASI KATTA HARF
    }

    init(wrappedValue: String) {
        self.matn = wrappedValue.capitalized
    }
}

struct Profil {
    @BoshHarfli var ism = "ali valiyev"
}

var profil = Profil()
print(profil.ism)   // "Ali Valiyev" (wrappedValue)
print(profil.$ism)  // "ALI VALIYEV" (projectedValue)

// SwiftUI da $state β€” Binding qaytaradi
// @State var ism = "" β†’ $ism = Binding<String>

@State va @Published qanday ishlaydi

SwiftUI ning @State va Combine ning @Published aslida property wrapper. @State qiymat o'zgarganda View ni qayta chizadi. @Published qiymat o'zgarganda ObservableObject orqali barcha kuzatuvchilarga signal yuboradi. Quyida ularning soddalashtirilgan ishlash tartibi ko'rsatilgan.

// ═══════════════════════════════════════
//  @STATE β€” soddalashtirilgan ko'rinish
//  (Apple ning haqiqiy implementatsiyasi murakkab)
// ═══════════════════════════════════════
// @State aslida shunday ishlaydi:
// 1. wrappedValue β€” qiymatni o'qish/yozish
// 2. projectedValue ($) β€” Binding qaytaradi
// 3. Qiymat o'zgarganda β€” SwiftUI View ni qayta chizadi

// ═══════════════════════════════════════
//  @PUBLISHED β€” soddalashtirilgan ko'rinish
// ═══════════════════════════════════════
// @Published aslida shunday ishlaydi:
// 1. wrappedValue β€” qiymatni o'qish/yozish
// 2. projectedValue ($) β€” Combine Publisher qaytaradi
// 3. willSet da β€” ObservableObject.objectWillChange signal yuboradi

// Misol: @Published ni tushunish
import Combine

@propertyWrapper
struct XabardorQiymat<T> {
    private var qiymat: T
    // Publisher β€” qiymat o'zgarganda signal
    let publisher = PassthroughSubject<T, Never>()

    var wrappedValue: T {
        get { qiymat }
        set {
            qiymat = newValue
            publisher.send(newValue)  // Kuzatuvchilarga xabar
        }
    }

    var projectedValue: PassthroughSubject<T, Never> {
        publisher
    }

    init(wrappedValue: T) {
        self.qiymat = wrappedValue
    }
}

Amaliy misollar

Property Wrapper ning chinakam kuchi amaliy misollarda namoyon bo'ladi. @UserDefaultsda β€” qiymatni avtomatik UserDefaults ga saqlash. @Tozalangan β€” matn atrofidagi bo'shliqlarni avtomatik olib tashlash. Bir marta yozib, ko'p joyda ishlatish mumkin.

// ═══════════════════════════════════════
//  @USERSDEFAULTS β€” avtomatik saqlash
// ═══════════════════════════════════════
@propertyWrapper
struct UserDefaultsda<T> {
    let kalit: String
    let boshlangich: T

    var wrappedValue: T {
        get { UserDefaults.standard.object(forKey: kalit) as? T ?? boshlangich }
        set { UserDefaults.standard.set(newValue, forKey: kalit) }
    }
}

struct Sozlamalar {
    @UserDefaultsda(kalit: "qorong'uRejim", boshlangich: false)
    var qorong'uRejim: Bool

    @UserDefaultsda(kalit: "tilKodi", boshlangich: "uz")
    var til: String
}

var soz = Sozlamalar()
soz.qorong'uRejim = true  // UserDefaults ga avtomatik saqlandi


// ═══════════════════════════════════════
//  @TRIMMED β€” bo'shliqlarni olib tashlash
// ═══════════════════════════════════════
@propertyWrapper
struct Tozalangan {
    private var matn: String = ""

    var wrappedValue: String {
        get { matn }
        set { matn = newValue.trimmingCharacters(in: .whitespacesAndNewlines) }
    }

    init(wrappedValue: String) {
        self.wrappedValue = wrappedValue
    }
}

struct RoyxatdanOtish {
    @Tozalangan var email = ""
    @Tozalangan var ism = ""
}

var forma = RoyxatdanOtish()
forma.email = "  ali@mail.com  "
print(forma.email)  // "ali@mail.com" β€” bo'shliqlar olib tashlandi

🎯 Topshiriq

@Parolga property wrapper yarating: kamida 8 ta belgi, kamida 1 ta raqam bo'lishi kerak. Agar qoida buzilsa β€” eski qiymatni saqlang. projectedValue sifatida Bool (parol kuchli/kuchsiz) qaytaring. $parol bilan tekshiring.

Buy mea coffee