October 17, 2026

7 Cara Mengelola State di SwiftUI: Perbandingan untuk iOS

Meja pengembang dengan laptop menampilkan kode dan catatan diagram state
State adalah inti aplikasi SwiftUI. Panduan ini membandingkan tujuh pendekatan pengelolaan state, dari @State hingga ObservableObject, agar kamu bisa memilih yang paling sesuai.

Mengelola state di SwiftUI sering membingungkan karena banyak pilihan property wrapper dan pola arsitektur. Setiap pendekatan punya konsekuensi pada performa, keterbacaan, dan kemudahan pengujian. Berikut tujuh cara mengelola state di SwiftUI, lengkap dengan contoh dan saran penggunaannya.

1. @State untuk state lokal yang sederhana

@State adalah pilihan pertama untuk menyimpan nilai yang hanya dipakai oleh satu view dan tidak perlu dibagikan. SwiftUI menyimpan nilai ini di luar struct view, sehingga perubahan memicu render ulang. Gunakan @State untuk hal seperti toggle, teks input sementara, atau indeks tab yang sedang aktif.

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

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

Keterbatasannya: @State tidak cocok untuk data yang harus bertahan saat view dihancurkan atau dibagikan ke banyak view. Jika kamu mulai mengoper binding ke banyak tempat, pertimbangkan pendekatan lain.

2. @Binding untuk meneruskan akses baca-tulis

@Binding memungkinkan child view mengubah nilai milik parent tanpa memiliki data itu sendiri. Ini berguna untuk komponen yang dapat digunakan ulang, seperti form field atau slider. Parent tetap menjadi sumber kebenaran, child hanya memegang referensi.

struct ParentView: View {
    @State private var isOn = false

    var body: some View {
        ToggleView(isOn: $isOn)
    }
}

struct ToggleView: View {
    @Binding var isOn: Bool

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

Binding membuat aliran data jelas, tetapi terlalu banyak binding berlapis bisa menyulitkan pelacakan. Untuk data yang kompleks, pertimbangkan observable object.

3. ObservableObject dan @StateObject untuk model referensi

Ketika state perlu dimiliki oleh sebuah objek yang memiliki logika dan bertahan selama view hidup, gunakan ObservableObject dengan @StateObject. @StateObject memastikan objek dibuat sekali dan tidak dibuat ulang setiap render. Ini adalah pilihan umum untuk view model atau pengelola data jaringan.

class CounterModel: ObservableObject {
    @Published var count = 0

    func increment() {
        count += 1
    }
}

struct CounterView: View {
    @StateObject private var model = CounterModel()

    var body: some View {
        Button("Tambah: (model.count)") {
            model.increment()
        }
    }
}

Perhatikan bahwa @ObservedObject berbeda: gunakan @ObservedObject ketika objek dimiliki oleh pihak lain dan diteruskan ke view. Kesalahan umum adalah memakai @ObservedObject untuk membuat objek di dalam view, yang menyebabkan objek dibuat ulang.

4. @EnvironmentObject untuk dependensi global

@EnvironmentObject menyuntikkan observable object ke seluruh hierarki view tanpa harus mengopernya secara eksplisit. Cocok untuk data yang memang global, seperti sesi pengguna, pengaturan aplikasi, atau keranjang belanja. Namun, penggunaan berlebihan membuat ketergantungan tersembunyi dan sulit dilacak.

class Session: ObservableObject {
    @Published var isLoggedIn = false
}

struct RootView: View {
    @StateObject private var session = Session()

    var body: some View {
        ContentView()
            .environmentObject(session)
    }
}

struct ContentView: View {
    @EnvironmentObject var session: Session

    var body: some View {
        Text(session.isLoggedIn ? "Masuk" : "Keluar")
    }
}

Selalu pastikan environment object disuntikkan sebelum view yang membutuhkannya, jika tidak aplikasi akan crash saat runtime.

5. @Environment untuk nilai sistem dan preferensi

@Environment membaca nilai dari environment SwiftUI, seperti color scheme, ukuran teks dinamis, atau mode presentasi. Kamu juga bisa mendefinisikan environment key sendiri untuk menyimpan preferensi yang tidak memerlukan ObservableObject. Ini ringan dan tidak memicu observasi objek.

struct ThemedView: View {
    @Environment(.colorScheme) private var colorScheme

    var body: some View {
        Text("Mode: (colorScheme == .dark ? "Gelap" : "Terang")")
    }
}

@Environment tidak cocok untuk state yang sering berubah dan perlu memicu logika bisnis. Gunakan untuk nilai yang bersifat konfigurasi atau bacaan sistem.

6. @Observable macro (iOS 17+) untuk observasi yang lebih efisien

Mulai iOS 17, Swift memperkenalkan macro @Observable yang menggantikan kebutuhan akan @Published dan ObservableObject dalam banyak kasus. Dengan @Observable, SwiftUI hanya merender ulang view yang membaca properti yang berubah, sehingga lebih efisien. Untuk aplikasi baru yang menargetkan iOS 17 ke atas, ini bisa menjadi pilihan default.

import Observation

@Observable
class CounterModel {
    var count = 0

    func increment() {
        count += 1
    }
}

struct CounterView: View {
    @State private var model = CounterModel()

    var body: some View {
        Button("Tambah: (model.count)") {
            model.increment()
        }
    }
}

Jika kamu masih mendukung iOS 16 atau lebih lama, tetap gunakan ObservableObject. Migrasi dapat dilakukan bertahap saat minimum deployment target dinaikkan.

7. Pola arsitektur: MVVM, TCA, atau state terpusat

Di atas property wrapper, ada pilihan arsitektur yang mempengaruhi cara state dikelola. MVVM memisahkan logika ke view model, cocok untuk tim yang sudah familiar dengan pola ini. TCA (The Composable Architecture) menawarkan state terpusat dan alur data satu arah, tetapi menambah kompleksitas dan dependency. Pendekatan state terpusat sederhana bisa menggunakan satu ObservableObject global untuk aplikasi kecil.

Pertimbangkan ukuran tim dan umur proyek. Untuk prototipe atau aplikasi kecil, @State dan ObservableObject sudah cukup. Untuk aplikasi besar dengan banyak fitur, arsitektur yang lebih terstruktur membantu menjaga konsistensi. Apa pun pilihannya, pastikan aliran data mudah dilacak dan diuji.

Ringkasan perbandingan

Pendekatan Kapan dipakai Catatan
@State State lokal sederhana Tidak untuk data yang dibagikan
@Binding Meneruskan akses ke child view Parent tetap sumber kebenaran
@StateObject Model referensi milik view Buat sekali, jangan ulang
@ObservedObject Model yang dimiliki pihak lain Hindari membuat objek di sini
@EnvironmentObject Dependensi global Hati-hati ketergantungan tersembunyi
@Environment Nilai sistem atau preferensi Ringan, tidak untuk logika bisnis
@Observable iOS 17+, observasi efisien Pengganti modern ObservableObject

Menguasai cara mengelola state di SwiftUI berarti memahami batas antara state lokal dan state yang dibagikan. Mulailah dari yang paling sederhana, lalu naikkan kompleksitas hanya jika perlu. Untuk eksperimen jaringan saat menguji state yang bergantung pada koneksi, alat seperti just-ping.com bisa membantu memeriksa latensi. Jika kamu ingin mempelajari lebih dalam tentang arsitektur aplikasi dan praktik pengembangan, sumber seperti offnow.org menyediakan materi yang bisa kamu eksplorasi.

Pertanyaan yang Sering Diajukan

Kapan sebaiknya menggunakan @StateObject daripada @ObservedObject?

Gunakan @StateObject ketika view memiliki objek tersebut dan bertanggung jawab atas siklus hidupnya. Gunakan @ObservedObject ketika objek dibuat di tempat lain dan diteruskan sebagai parameter.

Apakah @Observable bisa menggantikan semua penggunaan ObservableObject?

Untuk aplikasi yang menargetkan iOS 17 ke atas, @Observable dapat menggantikan ObservableObject dalam banyak skenario. Namun, jika kamu masih perlu mendukung versi iOS lebih lama, tetap gunakan ObservableObject.

Bagaimana cara menghindari terlalu banyak @EnvironmentObject?

Batasi @EnvironmentObject untuk dependensi yang benar-benar global. Untuk state yang hanya dipakai beberapa view, gunakan @StateObject dan oper secara eksplisit melalui initializer atau binding.