Memilih antara Observable dan Environment di SwiftUI biasanya muncul saat satu model mulai dibutuhkan di beberapa layar sekaligus. Observable memberi kamu objek eksplisit yang dioper lewat parameter, sementara Environment menyuntikkan nilai ke seluruh subtree tanpa perlu diteruskan manual. Perbedaan cara kerja keduanya menentukan seberapa mudah kode dirawat saat aplikasi tumbuh.
Dasar Perbedaan Observable vs Environment di SwiftUI
Sejak Swift 5.9, makro @Observable menggantikan pola ObservableObject dengan @Published. SwiftUI hanya memantau properti yang benar-benar dibaca oleh sebuah view, jadi render ulang lebih hemat daripada pendekatan lama. Objek hasil makro ini tetap berupa kelas biasa yang kamu buat dan simpan di suatu tempat.
Environment bekerja di lapisan berbeda. Kamu menempelkan nilai ke hierarki view lewat modifier .environment(), lalu view mana pun di bawahnya bisa membacanya dengan @Environment. Nilai itu tidak perlu melewati setiap initializer di antaranya.
Perbedaan intinya: Observable mengatur apa yang disimpan, Environment mengatur bagaimana nilai itu sampai ke view. Keduanya sering dipakai bersamaan.
Kapan Observable Lebih Tepat
Gunakan Observable saat sebuah model hanya dipakai satu atau dua layar dan kamu ingin alur datanya terlihat jelas di tanda tangan fungsi. Ketika view menerima objek lewat parameter, siapa membaca apa langsung terbaca dari kode.
- ViewModel yang menangani satu fitur, misalnya form pengaturan akun.
- Model yang perlu diuji terpisah tanpa membangun hierarki view.
- Objek yang siklus hidupnya terikat pada satu view lewat
@State.
Contoh sederhana menyimpan daftar tugas di dalam view:
import Observation
@Observable
final class TaskStore {
var items: [String] = []
func add(_ title: String) {
items.append(title)
}
}
struct TaskListView: View {
@State private var store = TaskStore()
var body: some View {
List(store.items, id: .self) { item in
Text(item)
}
}
}
Karena store dibuat dengan @State, SwiftUI mempertahankan instance selama view hidup dan membuangnya saat view hilang. Pola ini cocok untuk data yang tidak perlu bertahan lebih lama dari layarnya.
Kapan Environment Lebih Masuk Akal
Environment unggul ketika satu nilai dibutuhkan di banyak tempat yang berjauhan di pohon view. Contoh yang paling sering muncul adalah sesi pengguna, preferensi tema, atau konfigurasi jaringan. Meneruskan objek yang sama lewat lima lapis initializer hanya menambah noise.
Kamu tetap bisa memakai kelas @Observable sebagai isi Environment, jadi keduanya tidak bertentangan.
@Observable
final class Session {
var userName: String = "Tamu"
var isLoggedIn: Bool = false
}
struct RootView: View {
@State private var session = Session()
var body: some View {
ContentView()
.environment(session)
}
}
struct ProfileBadge: View {
@Environment(Session.self) private var session
var body: some View {
Text(session.userName)
}
}
Perhatikan bahwa ProfileBadge tidak perlu tahu siapa yang menyediakan sesi. Itu keuntungan sekaligus risikonya: dependensi jadi tersembunyi. Saat membaca view secara terpisah, kamu tidak langsung tahu dari mana session berasal.
Perbandingan Berdasarkan Kebutuhan Praktis
| Aspek | Observable | Environment |
|---|---|---|
| Kejelasan dependensi | Tinggi, tampak di parameter | Rendah, tersembunyi di hierarki |
| Jumlah view pemakai | Cocok untuk sedikit view | Cocok untuk banyak view tersebar |
| Kemudahan pengujian | Mudah, cukup buat instance | Perlu menyiapkan environment saat uji |
| Risiko lupa menyuntikkan | Kecil, compiler menegur | Ada, muncul sebagai crash saat runtime |
| Perubahan API internal | Terasa di setiap pemanggil | Tidak mengubah tanda tangan view |
Baris terakhir sering jadi penentu. Kalau kamu sering menambah parameter ke ViewModel dan lelah memperbarui semua pemanggil, Environment mengurangi gesekan itu. Sebaliknya, kalau kamu ingin perubahan dependensi langsung terlihat saat review kode, Observable lebih aman.
Untuk kebutuhan pengelolaan state yang lebih luas, termasuk interaksi dengan navigasi dan animasi, pendekatan ini sebaiknya dipadukan dengan pola lain. Panduan 7 cara mengelola state di SwiftUI memetakan pilihan dari yang paling lokal sampai yang global.
Menggabungkan Keduanya Tanpa Kekacauan
Pola yang lazim di aplikasi menengah: simpan objek jangka panjang di Environment, lalu oper objek spesifik fitur lewat parameter. Sesi pengguna, pengaturan aplikasi, dan layanan jaringan masuk Environment. ViewModel untuk satu layar tetap dibuat lokal dengan @State atau dioper dari parent.
Ada satu jebakan yang perlu diwaspadai. Menyimpan terlalu banyak hal di Environment membuat view sulit dipahami karena dependensinya tidak terlihat. Aturan praktisnya: kalau sebuah nilai hanya dipakai satu subtree kecil, oper lewat parameter. Kalau dipakai lintas fitur dan jarang berubah, Environment lebih rapi.
Environment bukan tempat sampah dependensi. Setiap nilai yang kamu taruh di sana menambah satu hal yang harus dicari pembaca kode ketika melacak sumber data.
Untuk data yang perlu bertahan antar sesi, Environment hanya menyelesaikan masalah penyampaian, bukan penyimpanan. Pilihan antara UserDefaults, file, dan SwiftData dibahas terpisah di perbandingan penyimpanan data formulir iOS. Sementara itu, kalau modelmu melakukan panggilan jaringan, pola konkurensi yang dipakai memengaruhi cara state diperbarui, seperti diuraikan dalam perbandingan async/await, Combine, dan callback.
Dampak ke Pengujian
Observable lebih ramah untuk unit test karena kamu bisa membuat instance langsung di dalam test tanpa ViewHost. Uji perilaku TaskStore.add cukup dengan memanggil method-nya dan memeriksa array.
Environment menuntut sedikit lebih banyak persiapan. Saat menguji view yang membaca Environment, kamu perlu menyuntikkan nilai pengganti. Untuk aplikasi dengan banyak layar, ini tetap bisa dikelola dengan membuat helper yang membungkus view dalam environment uji. Biayanya muncul sekali, lalu dipakai berulang.
Kalau tim kamu sering mengalami crash karena nilai Environment tidak ditemukan saat pratinjau atau test, itu tanda cakupan pemakaiannya terlalu luas atau penyuntikannya tidak konsisten di root.
Ringkasan
Observable cocok untuk model yang cakupannya sempit dan perlu terlihat jelas di tanda tangan fungsi. Environment cocok untuk nilai lintas fitur yang dipakai banyak view tersebar dan jarang berubah. Sebagian besar aplikasi memakai keduanya: Environment untuk sesi dan konfigurasi, Observable untuk ViewModel per layar. Pilih berdasarkan luas pemakaian dan seberapa besar kamu ingin dependensi terlihat saat membaca kode.
Pertanyaan yang Sering Diajukan
Apakah Observable bisa dipakai tanpa Environment?
Bisa. Buat instance dengan @State di view pemilik, lalu oper ke child lewat parameter atau @Bindable. Environment hanya diperlukan ketika kamu ingin menghindari penerusan manual ke banyak lapis view.
Apakah Environment hanya untuk objek kelas?
Tidak. Environment bisa menyimpan nilai struct, seperti preferensi tampilan atau konfigurasi. Untuk nilai yang berubah dan memicu render ulang, pastikan tipe tersebut memenuhi syarat yang dibutuhkan SwiftUI.
Apa yang terjadi kalau nilai Environment tidak disuntikkan?
Aplikasi akan gagal saat runtime ketika view mencoba membaca nilai yang tidak ada. Karena itu, suntikkan nilai di root hierarki dan hindari membaca Environment opsional tanpa penanganan yang jelas.
More Stories
Async/Await vs Combine vs Callback di Swift: Panduan Memilih
7 Cara Mengelola State di SwiftUI: Perbandingan untuk iOS