Property Wrapper: konsep kunci, sintaks dan contoh di SwiftUI

Penulis: IT Sectr Diterbitkan: 2026-06-19 Waktu membaca: 8 mnt

Property Wrapper adalah mekanisme Swift yang menambahkan lapisan logika pada akses dan perubahan properti tanpa duplikasi kode. Di SwiftUI, Property Wrapper menjadi dasar pengelolaan status tampilan: @State, @Binding, @ObservedObject, @StateObject dan @Environment. Menurut dokumentasi Swift (2025), pembungkus properti mengurangi kode boilerplate dalam proyek rata-rata sebesar 40%. Pemahaman tentang Property Wrapper diperlukan bagi setiap pengembang iOS untuk bekerja secara efektif dengan framework.

Poin utama

  • Property Wrapper — konstruksi sintaksis Swift yang menambahkan logika ke getter dan setter properti
  • @State — pembungkus untuk status lokal yang dimiliki oleh satu tampilan
  • @Binding — koneksi dua arah dengan sumber data dari tampilan induk
  • @ObservedObject — observasi ObservableObject eksternal tanpa kepemilikan
  • @StateObject — pembuatan dan kepemilikan ObservableObject di tingkat tampilan saat ini

Apa itu Property Wrapper?

Property Wrapper adalah konstruksi bahasa Swift yang muncul di versi 5.1, yang memungkinkan enkapsulasi logika akses properti ke dalam tipe terpisah. Alih-alih menulis getter dan setter berulang di setiap kelas, pengembang mendeklarasikan pembungkus sekali dan menerapkannya melalui anotasi @ sebelum tipe. Swift secara otomatis membungkus properti ke dalam tipe yang ditentukan, memanggil metode wrappedValue dan projectedValue saat membaca dan menulis. Menurut Apple (WWDC 2019), Property Wrapper telah menjadi abstraksi kunci untuk SwiftUI.

Pembungkus properti adalah struktur atau kelas dengan atribut @propertyWrapper. Di dalamnya, tipe tersebut harus mengimplementasikan properti wrappedValue yang mengembalikan dan menetapkan nilai saat ini. Kompiler Swift mengganti referensi ke properti asli dengan panggilan wrappedValue, sepenuhnya menyembunyikan implementasi dari kode pemanggil. Selain itu, dapat didefinisikan projectedValue — proyeksi yang dapat diakses melalui simbol $.

Keunggulan Property Wrapper terletak pada penggunaan kembali logika. Misalnya, dapat dibuat pembungkus untuk validasi email, penyimpanan nilai cache, atau sinkronisasi dengan penyimpanan — dan menerapkannya ke properti mana pun dalam proyek. Di SwiftUI, konsep ini digunakan di mana-mana: setiap mekanisme pengelolaan status diimplementasikan sebagai Property Wrapper terpisah.

Bagaimana Property Wrapper bekerja di Swift?

Saat mendeklarasikan properti dengan anotasi @WrapperType var value: T, kompiler Swift mentransformasi kode. Ia membuat instance WrapperType dan menghasilkan akses ke properti melalui wrappedValue. Kode sumber let x = value berubah menjadi let x = _value.wrappedValue, dan value = newValue menjadi _value.wrappedValue = newValue. Transformasi ini terjadi pada tahap kompilasi, tanpa overhead runtime.

swift
@propertyWrapper
struct Capitalized {
    private var text: String

    var wrappedValue: String {
        get { text }
        set { text = newValue.capitalized }
    }

    init(initialValue: String) {
        text = initialValue.capitalized
    }
}

Dalam listing ditunjukkan pembungkus Capitalized, yang secara otomatis memformat teks dengan huruf kapital. Saat menetapkan nilai, setter memanggil capitalized sebelum menyimpan. Sekarang properti apa pun dengan anotasi @Capitalized hanya akan menyimpan teks yang diformat dengan benar. Pendekatan ini sepenuhnya menghilangkan duplikasi kode validasi dan pemformatan.

Proyeksi (projectedValue) — saluran komunikasi tambahan, dapat diakses melalui prefiks $. Di SwiftUI, kemampuan ini digunakan di mana-mana: $state memberikan Binding, dan $observedObject mengembalikan Published.Publisher. Proyeksi memungkinkan pembungkus menyediakan antarmuka yang diperluas tanpa mengubah akses utama ke nilai.

Property Wrapper utama di SwiftUI

SwiftUI mencakup lima Property Wrapper bawaan untuk pengelolaan status: @State, @Binding, @ObservedObject, @StateObject dan @Environment. Masing-masing memecahkan tugas tertentu dan digunakan dalam skenario berbeda. @State ditujukan untuk data lokal sederhana, @Binding — untuk mengirimkan referensi data ke tampilan anak, @ObservedObject dan @StateObject — untuk objek kompleks, @Environment — untuk nilai sistem dari hierarki.

WrapperTujuanKepemilikan
@StateStatus lokal satu tampilanTampilan saat ini
@BindingKoneksi dua arah dengan indukTampilan induk
@ObservedObjectObservasi objek eksternalPemilik eksternal
@StateObjectPembuatan ObservableObjectTampilan saat ini
@EnvironmentNilai sistem dari hierarkiLingkungan SwiftUI

Pemilihan Property Wrapper spesifik tergantung pada sumber data dan siklus hidupnya. Jika data milik satu tampilan dan tidak diperlukan oleh komponen anak — gunakan @State. Jika tampilan anak perlu mengubah data induk — terapkan @Binding. Untuk objek yang digunakan di beberapa tampilan, @ObservedObject dan @StateObject cocok.

@State: status lokal tampilan

@State adalah Property Wrapper untuk menyimpan status lokal di dalam satu tampilan. SwiftUI secara otomatis mengelola memori untuk properti @State dan menggambar ulang tampilan pada setiap perubahan. @State cocok untuk tipe sederhana (String, Int, Bool, enum) dan struktur yang hanya dimiliki oleh tampilan saat ini. Saat nilai berubah, SwiftUI menjalankan ulang properti body.

swift
struct CounterView: View {
    @State private var count: Int = 0

    var body: some View {
        VStack {
            Text("Jumlah: \(count)")
            Button("Tambah") {
                count += 1
            }
        }
    }
}

Dalam contoh, properti @State count menyimpan nilai penghitung saat ini. SwiftUI membuat area penyimpanan untuk properti ini di heap dan menghubungkannya dengan siklus hidup CounterView. Saat tombol ditekan, count bertambah 1, SwiftUI mendeteksi perubahan dan menjalankan ulang body, menampilkan nilai baru. Penting: @State tidak boleh digunakan untuk tipe referensi kompleks — untuk itu digunakan @StateObject dan @ObservedObject.

@Binding: koneksi dua arah antar tampilan

@Binding membuat referensi ke sumber data milik tampilan lain. Binding tidak menyimpan nilai secara mandiri — ia membaca dan menulis data melalui @State, @StateObject atau Binding lain yang dikirim dari induk. Ini memungkinkan komponen anak mengubah status leluhur tanpa kepemilikan langsung data dan tanpa callback.

swift
struct ToggleSwitch: View {
    @Binding var isOn: Bool

    var body: some View {
        Toggle("Switch", isOn: $isOn)
    }
}

Dalam listing, ToggleSwitch menerima @BindingBool dari tampilan induk. Induk membuat @State var isToggleOn = false dan mengirimkan $isToggleOn ke inisialisator ToggleSwitch. Saat pengguna mengalihkan Toggle di dalam tampilan anak, perubahan segera tercermin di @State induk. Mekanisme Binding sepenuhnya menghilangkan kebutuhan akan delegat atau closure untuk mengirimkan perubahan ke atas hierarki.

@ObservedObject dan @StateObject: data eksternal

@ObservedObject adalah Property Wrapper untuk mengamati instance ObservableObject yang dikirim dari luar. Tampilan bukan pemilik objek ini — objek dibuat di komponen induk atau disuntikkan melalui Environment. Ketika properti @Published di dalam ObservableObject berubah, SwiftUI menggambar ulang semua tampilan yang berlangganan melalui @ObservedObject.

@StateObject adalah pembungkus untuk membuat dan memiliki ObservableObject langsung di tampilan. Tidak seperti @ObservedObject, @StateObject menjamin instance tunggal objek selama seluruh siklus hidup tampilan. Bahkan jika SwiftUI membuat ulang struktur tampilan (yang sering terjadi), @StateObject mempertahankan objek yang ada dan tidak memanggil inisialisator lagi.

swift
class UserSettings: ObservableObject {
    @Published var username: String = "Guest"
}

struct ProfileView: View {
    @StateObject var settings = UserSettings()

    var body: some View {
        ChildProfileView(settings: settings)
    }
}

struct ChildProfileView: View {
    @ObservedObject var settings: UserSettings

    var body: some View {
        Text("Halo, \(settings.username)")
    }
}

Dalam contoh, ProfileView membuat UserSettings melalui @StateObject, menjadi pemilik objek. ChildProfileView menerima instance yang sama melalui @ObservedObject — mengamati, tetapi tidak mengelola siklus hidup. Saat username berubah, kedua tampilan diperbarui. Jika ChildProfileView menggunakan @StateObject alih-alih @ObservedObject, setiap render akan membuat instance baru dengan nilai awal.

Aturan kunci: @StateObject diterapkan di tampilan yang membuat objek (sumber kebenaran), dan @ObservedObject di tampilan yang menerima objek jadi dari induk. Pelanggaran aturan ini menyebabkan hilangnya status atau pembuatan ulang data yang tidak terduga.

Membuat Property Wrapper kustom

Swift memungkinkan pembuatan Property Wrapper kustom untuk setiap logika akses properti yang berulang. Cukup mendeklarasikan struktur atau kelas dengan atribut @propertyWrapper dan mengimplementasikan wrappedValue. Di bawah ini ditunjukkan pembungkus UserDefaultsWrapper yang secara otomatis menyinkronkan nilai dengan UserDefaults.

swift
@propertyWrapper
struct UserDefaultsWrapper<T> {
    let key: String
    let defaultValue: T

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

struct AppConfig {
    @UserDefaultsWrapper(key: "theme", defaultValue: "light")
    var theme: String
}

Pembungkus UserDefaultsWrapper menggunakan generic T untuk bekerja dengan tipe data apa pun yang didukung oleh UserDefaults. Getter membaca nilai berdasarkan kunci, setter menulis. Penerapan @UserDefaultsWrapper(key:defaultValue:) ke properti theme secara otomatis menghubungkannya dengan penyimpanan — semua logika kerja dengan UserDefaults tersembunyi di dalam pembungkus. Ini adalah contoh tipikal pengurangan kode boilerplate dengan Property Wrapper.

Saat membuat pembungkus kustom, penting untuk mempertimbangkan kinerja. Karena getter dan setter dipanggil setiap kali mengakses properti, operasi I/O berat tidak boleh ditempatkan di wrappedValue. Untuk penyimpanan data asinkron, lebih baik menggabungkan Property Wrapper dengan ObservableObject dan @Published.

Pertanyaan yang sering diajukan

Apa perbedaan antara @State dan @StateObject?

@State ditujukan untuk tipe sederhana (String, Int, Bool) dan struktur, sedangkan @StateObject untuk tipe referensi yang mengimplementasikan ObservableObject. @State menyimpan nilai langsung di SwiftUI, @StateObject mengelola instance kelas di heap.

Bisakah @Binding digunakan tanpa @State?

Ya, @Binding dapat dibuat dari @StateObject, @ObservedObject, atau Binding lain menggunakan proyeksi $. Binding juga diinisialisasi dari ObservableObject melalui $object.$publishedProperty atau dari InlineBinding melalui Binding.constant(value).

Property Wrapper mana yang dipilih untuk data yang digunakan di seluruh aplikasi?

Untuk data global, gunakan @EnvironmentObject atau suntikkan ObservableObject melalui EnvironmentValues. @StateObject cocok untuk tampilan akar dengan transmisi selanjutnya melalui @ObservedObject ke komponen anak.

Mengapa @ObservedObject dapat membuat ulang data?

@ObservedObject tidak memiliki objek — jika tampilan induk dibuat ulang dan mengirimkan instance baru, @ObservedObject akan beralih ke instance tersebut. Untuk menghindari hilangnya status, tampilan pemilik harus menggunakan @StateObject.

Bisakah Property Wrapper dibuat untuk operasi asinkron?

Ya, tetapi lebih mudah menggunakan kombinasi ObservableObject dengan @Published dan fungsi async di dalam kelas. Property Wrapper pada dasarnya sinkron — wrappedValue dihitung setiap kali diakses, yang tidak cocok untuk operasi jangka panjang.

Kesimpulan

  • Property Wrapper — konstruksi sintaksis Swift 5.1 untuk enkapsulasi logika akses properti tanpa duplikasi kode
  • @State — status lokal untuk tipe sederhana, secara otomatis menggambar ulang tampilan saat berubah
  • @Binding — referensi ke data tampilan induk tanpa kepemilikan, dapat diakses melalui proyeksi $
  • @StateObject — kepemilikan ObservableObject dengan jaminan instance tunggal selama siklus hidup
  • @ObservedObject — observasi ObservableObject yang dikirim dari komponen induk
  • @Environment — akses ke objek sistem lingkungan SwiftUI
  • Pembungkus kustom dibuat melalui atribut @propertyWrapper dan implementasi wrappedValue

Kami akan mengembangkan aplikasi seluler turnkey

IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.

Diskusikan proyek

Baca juga