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 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.
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.
@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
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.
| Wrapper | Tujuan | Kepemilikan |
|---|---|---|
| @State | Status lokal satu tampilan | Tampilan saat ini |
| @Binding | Koneksi dua arah dengan induk | Tampilan induk |
| @ObservedObject | Observasi objek eksternal | Pemilik eksternal |
| @StateObject | Pembuatan ObservableObject | Tampilan saat ini |
| @Environment | Nilai sistem dari hierarki | Lingkungan 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 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.
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 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.
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 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.
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.
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.
@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
@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.
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).
Untuk data global, gunakan @EnvironmentObject atau suntikkan ObservableObject melalui EnvironmentValues. @StateObject cocok untuk tampilan akar dengan transmisi selanjutnya melalui @ObservedObject ke komponen anak.
@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.
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
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.
Baca juga