Deferred Navigation adalah pola navigasi tertunda di mana perpindahan ke layar berikutnya terjadi setelah operasi asinkron selesai, bukan langsung pada saat tindakan pengguna. Menurut Android Developers (2024), deferred navigation memungkinkan menghindari race condition antara navigasi dan pemuatan data, serta menyederhanakan penanganan transisi dari notifikasi push dan Deeplink. Perbedaan utama — rute dihitung setelah semua data yang diperlukan tersedia.
Hal utama
Deferred Navigation adalah pola arsitektur di mana keputusan navigasi ditunda hingga semua data yang diperlukan tersedia. Berbeda dengan transisi langsung, di mana pengguna menekan tombol dan langsung masuk ke layar baru, deferred navigation memisahkan peristiwa pemicu dan transisi aktual, menambahkan operasi asinkron di antaranya.
Secara arsitektur, Deferred Navigation didasarkan pada perubahan status: penekanan tombol memulai proses asinkron, dan langganan pada hasilnya memicu navigasi. Ini sangat penting dalam aplikasi dengan arsitektur MVVM atau MVI, di mana ViewModel mengelola status, dan View (Activity, Fragment, SwiftUI View) berlangganan perubahan dan bereaksi dengan transisi. Pendekatan ini menghilangkan ketergantungan langsung antara UI dan logika navigasi.
Menurut Google I/O 2023, deferred navigation direkomendasikan untuk semua skenario di mana navigasi bergantung pada hasil permintaan jaringan, pemeriksaan otorisasi, pemuatan konfigurasi, atau izin. Pola ini juga wajib saat memproses Deeplink, ketika aplikasi harus terlebih dahulu dimulai, memuat layar root, dan baru kemudian beralih ke rute target.
Deferred Navigation diterapkan dalam skenario di mana transisi langsung menyebabkan status layar yang salah atau kesalahan pemuatan. Mari kita lihat empat kasus utama di mana deferred navigation wajib.
Jika pengguna mengeklik konten yang dilindungi, aplikasi harus terlebih dahulu memeriksa token akses. Navigasi langsung ke layar konten akan menyebabkan layar kosong atau kesalahan 401 jika token kedaluwarsa. Deferred navigation memeriksa token, dan hanya jika berhasil — beralih ke layar target. Jika gagal — mengarahkan ke layar masuk.
Ketika aplikasi dibuka melalui tautan eksternal, pertama-tama layar root harus dimuat, status navigasi dipulihkan, dan baru kemudian transisi melalui Deeplink dilakukan. Navigasi langsung ke layar target tanpa konteks root akan menyebabkan anomali: tumpukan navigasi kosong atau back stack rusak.
Saat mengetuk notifikasi push, aplikasi dapat berada dalam tiga status: tertutup, di latar belakang, atau aktif. Deferred Navigation menentukan status aplikasi, memuat konten yang diperlukan, dan baru kemudian menampilkan layar target. iOS memungkinkan pemrosesan skenario ini melalui UNNotificationContentExtension.
Jika fungsionalitas layar dikelola oleh feature flag dari server, deferred navigation memungkinkan meminta konfigurasi terlebih dahulu dan baru kemudian menampilkan layar. Jika fitur dinonaktifkan, pengguna melihat konten alternatif atau placeholder alih-alih layar kosong.
| Skenario | Navigasi langsung | Deferred Navigation |
|---|---|---|
| Otorisasi | Layar kosong saat token kedaluwarsa | Pengalihan ke login |
| Deeplink | Back stack rusak | Tumpukan navigasi benar |
| Push | Pemuatan tanpa konteks | Data siap sebelum transisi |
| Feature flag | Menampilkan fungsi tidak tersedia | Placeholder atau alternatif |
Navigasi langsung (direct navigation) — pendekatan tradisional di mana transisi dilakukan segera sebagai respons terhadap peristiwa. Pengguna menekan tombol, dan router UI langsung mengganti layar. Pendekatan ini sederhana dan dapat diprediksi, tetapi terbatas dalam skenario yang membutuhkan data dari server atau pemeriksaan kondisi.
Deferred Navigation menambahkan lapisan berupa status asinkron. Peristiwa pengguna memulai operasi, dan langganan pada hasilnya mengelola navigasi. Ini meningkatkan kompleksitas kode, tetapi memberikan fleksibilitas: pemicu yang sama dapat mengarah ke layar yang berbeda tergantung pada data yang dimuat.
Pilihan antara dua pendekatan tergantung pada persyaratan: jika untuk menampilkan layar tidak diperlukan data asinkron — gunakan navigasi langsung. Jika layar bergantung pada hasil permintaan, otorisasi, atau kondisi eksternal — deferred navigation wajib. Pendekatan hibrida, di mana sebagian transisi bersifat langsung dan sebagian tertunda, adalah praktik paling umum dalam aplikasi produksi.
Android Jetpack menyediakan mekanisme untuk mengimplementasikan Deferred Navigation di tingkat arsitektur. Ide utama — ViewModel mengelola status, dan Activity atau Fragment berlangganan perubahan dan memicu navigasi melalui NavController.
StateFlow di Kotlin coroutines — alat ideal untuk deferred navigation. ViewModel memperbarui StateFlow dengan peristiwa navigasi, dan Activity mengamatinya dan melakukan transisi. Setelah peristiwa diproses, StateFlow dibersihkan, mencegah navigasi berulang.
class MainViewModel : ViewModel() {
private val _navigation = MutableSharedFlow<NavigationEvent>()
val navigation: SharedFlow<NavigationEvent> = _navigation
fun onDeepLinkReceived(link: String) {
viewModelScope.launch {
val data = resolveDeepLink(link)
_navigation.emit(NavigationEvent.GoToScreen(data))
}
}
}
Di Activity, langganan navigasi memicu NavController dengan rute dari ViewModel. Untuk mencegah navigasi berulang saat rotasi layar, digunakan wrapper NavigationEventWrapper yang memproses peristiwa hanya sekali. Jetpack Navigation 2.7+ mendukung Safe Args untuk pengiriman argumen yang aman secara tipe.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm: MainViewModel by viewModels()
repeatOnLifecycle(Lifecycle.State.STARTED) {
vm.navigation.collect { event ->
when (event) {
is NavigationEvent.GoToScreen ->
findNavController(R.id.nav_host)
.navigate(event.route)
}
}
}
}
}
iOS tidak memiliki Navigation Component bawaan yang mirip dengan Android Jetpack, sehingga pengembang mengimplementasikan Deferred Navigation melalui Coordinator Pattern yang dikombinasikan dengan Combine atau async/await. Coordinator mengelola tumpukan layar dan membuat keputusan navigasi berdasarkan data yang dimuat.
Coordinator — objek yang mengelola navigasi antar ViewController. Dalam kombinasi dengan Combine, ViewModel mempublikasikan peristiwa melalui PassthroughSubject, dan Coordinator berlangganan padanya dan melakukan transisi. Pendekatan ini sepenuhnya memisahkan UI dari logika navigasi dan sesuai dengan rekomendasi Apple untuk arsitektur aplikasi.
final class AppCoordinator {
private var cancellables = Set<AnyCancellable>()
func start(viewModel: MainViewModel) {
viewModel.$navigationDestination
.compactMap { $0 }
.sink { [weak self] destination in
self?.navigateTo(destination)
}
.store(in: &cancellables)
}
}
Swift 5.5 memperkenalkan structured concurrency, yang memungkinkan implementasi navigasi tertunda melalui async/await tanpa Combine. ViewModel menyediakan fungsi async yang mengembalikan rute setelah pemuatan data. Coordinator memanggil fungsi ini di Task dan melakukan transisi sesuai rute yang diterima.
class AuthViewModel: ObservableObject {
func resolveDeeplink(_ url: URL) async -> AppRoute? {
guard let token = await AuthService.shared.getValidToken() else { return .login }
return await DeeplinkRouter.resolve(url, token: token)
}
}
// Di Coordinator:
Task {
if let route = await viewModel.resolveDeeplink(url) {
navigateTo(route)
}
}
Deferred Navigation menyederhanakan penanganan skenario asinkron, tetapi memerlukan pendekatan disiplin dalam pengelolaan status. Mari kita lihat kesalahan utama yang dilakukan pengembang saat mengimplementasikan deferred navigation.
Kesalahan paling umum — mencoba melakukan deferred navigation sebelum layar root sepenuhnya diinisialisasi dan NavController atau Coordinator siap untuk transisi. Di Android ini menyebabkan IllegalStateException, di iOS — status UI yang tidak terdefinisi. Solusi — pastikan siklus hidup komponen berada dalam status STARTED atau RESUMED sebelum memulai navigasi.
Jika StateFlow atau Subject tidak membersihkan peristiwa setelah diproses, saat kembali ke layar sebelumnya pengguna dapat secara otomatis diarahkan kembali ke layar yang sama. Gunakan SharedFlow dengan replay=0 di Android atau CurrentValueSubject dengan nil setelah diproses di iOS, agar peristiwa navigasi hanya dipicu satu kali.
Gunakan satu komponen pusat untuk seluruh navigasi dalam aplikasi. Ketika setiap Activity, Fragment atau ViewController memiliki pengontrol navigasinya sendiri, deferred navigation antara bagian aplikasi yang berbeda menjadi kacau. Satu Coordinator memudahkan debugging dan pengujian skenario navigasi.
Deferred Navigation lebih sulit diuji daripada navigasi langsung karena operasi asinkron memperkenalkan faktor waktu. Gunakan TestDispatcher di Android (kotlinx-coroutines-test) dan XCTestExpectation di iOS untuk simulasi pemuatan data dan memeriksa bahwa navigasi dilakukan sesuai rute yang diharapkan. Mock layanan otorisasi dan deeplink untuk pengujian terisolasi setiap skenario.
Pertanyaan yang sering diajukan
Deferred Navigation adalah pola transisi tertunda yang dapat diterapkan dalam skenario asinkron apa pun. Deep Link adalah salah satu pemicu untuk deferred navigation, tetapi bukan satu-satunya. Otorisasi dan feature flag juga menggunakan navigasi tertunda.
Ya, sebagian besar aplikasi menggunakan pendekatan hibrida. Layar daftar produk (tanpa ketergantungan asinkron) dapat menggunakan navigasi langsung, dan layar detail dengan pemuatan data — deferred. Pembagian ditentukan oleh arsitektur layar spesifik.
Gunakan SharedFlow tanpa replay di Android dan combineLatest tanpa buffering di iOS. Batalkan langganan sebelumnya saat pemicu baru. Ini menjamin bahwa hanya peristiwa navigasi terakhir yang diproses.
Ya, di Compose deferred navigation diimplementasikan melalui langganan ke StateFlow ViewModel dan pemanggilan NavController.navigate di LaunchedEffect. Google merekomendasikan penggunaan Navigation Compose dengan model peristiwa untuk skenario deferred.
Tunda navigasi hingga aplikasi kembali aktif. Di Android gunakan Lifecycle.State.STARTED untuk menyaring peristiwa. Di iOS periksa UIApplication.State di blok Combine atau async/await.
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