Deferred Navigation — co to je, principy a implementace

Autor: IT Sectr Publikováno: 2026-06-10 Doba čtení: 9 min

Deferred Navigation je vzor odložené navigace, při kterém přechod na další obrazovku nastává po dokončení asynchronní operace, nikoli přímo v okamžiku akce uživatele. Podle Android Developers (2024) umožňuje deferred navigation vyhnout se závodním stavům mezi navigací a načítáním dat a také zjednodušuje zpracování přechodů z push oznámení a Deeplink. Klíčový rozdíl — trasa se vypočítá až po zpřístupnění všech potřebných dat.

Hlavní body

  • Deferred Navigation — odložená navigace, kde se přechod provádí po dokončení asynchronních operací
  • Přímá navigace zpracovává přechod okamžitě, deferred — čeká na data
  • Typické scénáře: autorizace, Deeplink, push oznámení, načítání konfigurace
  • V Androidu se implementuje přes Navigation Component s callbacky a StateFlow
  • V iOS se používá Combine nebo async/await s koordinátorem navigace

Co je Deferred Navigation

Deferred Navigation je architektonický vzor, při kterém je rozhodnutí o navigaci odloženo, dokud nejsou k dispozici všechna potřebná data. Na rozdíl od přímého přechodu, kde uživatel stiskne tlačítko a okamžitě se dostane na novou obrazovku, deferred navigation odděluje spouštěcí událost a skutečný přechod a vkládá mezi ně asynchronní operaci.

Architektonicky je Deferred Navigation založena na změně stavu: stisknutí tlačítka iniciuje asynchronní proces a přihlášení k odběru jeho výsledku spouští navigaci. To je zvláště důležité v aplikacích s architekturou MVVM nebo MVI, kde ViewModel spravuje stav a View (Activity, Fragment, SwiftUI View) se přihlašuje ke změnám a reaguje přechodem. Tento přístup odstraňuje přímou závislost mezi UI a logikou navigace.

Podle Google I/O 2023 je deferred navigation doporučena pro všechny scénáře, kde navigace závisí na výsledku síťového požadavku, ověření autorizace, načtení konfigurace nebo oprávnění. Vzor je také povinný při zpracování Deeplink, když aplikace musí nejprve spustit, načíst kořenovou obrazovku a teprve poté přejít na cílovou trasu.

Kdy je potřeba odložená navigace

Deferred Navigation se používá ve scénářích, kde přímý přechod vede k nesprávnému stavu obrazovky nebo chybám načítání. Podívejme se na čtyři hlavní případy, kdy je deferred navigation povinná.

Autorizace a autentizace

Pokud uživatel klikne na chráněný obsah, aplikace musí nejprve zkontrolovat přístupový token. Přímá navigace na obrazovku obsahu povede k prázdné obrazovce nebo chybě 401, pokud token vypršel. Deferred navigation zkontroluje token a pouze při úspěchu přejde na cílovou obrazovku. Při neúspěchu přesměruje na přihlašovací obrazovku.

Zpracování Deeplink

Když je aplikace otevřena prostřednictvím externího odkazu, je nejprve třeba načíst kořenovou obrazovku, obnovit stav navigace a teprve poté provést přechod přes Deeplink. Přímá navigace na cílovou obrazovku bez kořenového kontextu povede k anomáliím: prázdnému zásobníku navigace nebo poškozenému back stack.

Push oznámení s obsahem

Při klepnutí na push oznámení může být aplikace ve třech stavech: zavřená, na pozadí nebo aktivní. Deferred Navigation určuje stav aplikace, načte potřebný obsah a teprve poté zobrazí cílovou obrazovku. iOS umožňuje zpracovat tento scénář přes UNNotificationContentExtension.

Dynamický feature flag

Pokud je funkčnost obrazovky řízena feature flag ze serveru, deferred navigation umožňuje nejprve vyžádat konfiguraci a teprve poté zobrazit obrazovku. Pokud je funkce vypnuta, uživatel vidí alternativní obsah nebo placeholder místo prázdné obrazovky.

ScénářPřímá navigaceDeferred Navigation
AutorizacePrázdná obrazovka při vypršelém tokenuPřesměrování na přihlášení
DeeplinkPoškozený back stackSprávný zásobník navigace
PushNačítání bez kontextuData připravena před přechodem
Feature flagZobrazení nedostupné funkcePlaceholder nebo alternativa

Deferred Navigation vs přímá navigace

Přímá navigace (direct navigation) — tradiční přístup, při kterém se přechod provádí okamžitě jako reakce na událost. Uživatel stiskne tlačítko a směrovač UI okamžitě přepne obrazovku. Tento přístup je jednoduchý a předvídatelný, ale omezený ve scénářích, kde jsou potřeba data ze serveru nebo kontrola podmínek.

Deferred Navigation přidává vrstvu v podobě asynchronního stavu. Událost uživatele spouští operaci a přihlášení k odběru výsledku řídí navigaci. To zvyšuje složitost kódu, ale poskytuje flexibilitu: stejná spouštěč může vést k různým obrazovkám v závislosti na načtených datech.

Volba mezi dvěma přístupy závisí na požadavcích: pokud pro zobrazení obrazovky nejsou potřeba asynchronní data — použijte přímou navigaci. Pokud obrazovka závisí na výsledku požadavku, autorizaci nebo externích podmínkách — je deferred navigation povinná. Hybridní přístup, kde část přechodů je přímá a část odložená, je nejběžnější praxí v produkčních aplikacích.

Implementace v Androidu: Navigation Component a ViewModel

Android Jetpack poskytuje mechanismy pro implementaci Deferred Navigation na architektonické úrovni. Hlavní myšlenka — ViewModel spravuje stav a Activity nebo Fragment se přihlašuje ke změnám a spouští navigaci přes NavController.

Deferred Navigation s StateFlow

StateFlow v Kotlin coroutines — ideální nástroj pro deferred navigation. ViewModel aktualizuje StateFlow navigační událostí a Activity ji pozoruje a provádí přechod. Jakmile je událost zpracována, StateFlow je vymazán, čímž se zabrání opakované navigaci.

kotlin
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))
        }
    }
}

Deferred Navigation s NavController

V Activity přihlášení k odběru navigace spouští NavController s trasou z ViewModel. Aby se zabránilo opakované navigaci při otáčení obrazovky, používá se wrapper NavigationEventWrapper, který zpracovává událost pouze jednou. Jetpack Navigation 2.7+ podporuje Safe Args pro typově bezpečné předávání argumentů.

kotlin
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)
                }
            }
        }
    }
}

Implementace v iOS: Coordinator Pattern a Combine

iOS nemá vestavěný Navigation Component podobný Android Jetpack, proto vývojáři implementují Deferred Navigation přes Coordinator Pattern v kombinaci s Combine nebo async/await. Coordinator spravuje zásobník obrazovek a rozhoduje o navigaci na základě načtených dat.

Coordinator s Combine

Coordinator — objekt, který spravuje navigaci mezi ViewController. V kombinaci s Combine ViewModel publikuje události přes PassthroughSubject a Coordinator se k nim přihlašuje a provádí přechod. Tento přístup zcela odděluje UI od logiky navigace a je v souladu s doporučeními Apple pro architekturu aplikací.

swift
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)
    }
}

Deferred Navigation s async/await

Swift 5.5 zavedl structured concurrency, který umožňuje implementovat odloženou navigaci přes async/await bez Combine. ViewModel poskytuje async funkci, která po načtení dat vrací trasu. Coordinator volá tuto funkci v Task a provádí přechod podle získané trasy.

swift
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)
    }
}

// V Coordinator:
Task {
    if let route = await viewModel.resolveDeeplink(url) {
        navigateTo(route)
    }
}

Typické chyby a best practices

Deferred Navigation zjednodušuje zpracování asynchronních scénářů, ale vyžaduje disciplinovaný přístup ke správě stavu. Podívejme se na hlavní chyby, kterých se vývojáři dopouštějí při implementaci deferred navigation.

Chyba: navigace před dokončením inicializace

Nejčastější chyba — pokus o provedení deferred navigation před tím, než je kořenová obrazovka plně inicializována a NavController nebo Coordinator je připraven k přechodu. V Androidu to vede k IllegalStateException, v iOS — k nedefinovanému stavu UI. Řešení — ujistěte se, že životní cyklus komponenty je ve stavu STARTED nebo RESUMED před spuštěním navigace.

Chyba: opakovaná navigace při návratu na obrazovku

Pokud StateFlow nebo Subject nevyčistí událost po zpracování, při návratu na předchozí obrazovku může být uživatel automaticky přesměrován znovu na stejnou obrazovku. Použijte SharedFlow s replay=0 na Androidu nebo CurrentValueSubject s nil po zpracování na iOS, aby se navigační událost spustila pouze jednou.

Best Practice: jediný NavController nebo Coordinator

Použijte jeden centrální komponent pro celou navigaci v aplikaci. Když má každé Activity, Fragment nebo ViewController svůj vlastní navigační ovladač, deferred navigation mezi různými částmi aplikace se stává chaotickou. Jeden Coordinator usnadňuje ladění a testování navigačních scénářů.

Best Practice: testování scénářů odložené navigace

Deferred Navigation se testuje hůře než přímá navigace, protože asynchronní operace vnášejí časový faktor. Použijte TestDispatcher v Androidu (kotlinx-coroutines-test) a XCTestExpectation v iOS pro simulaci načítání dat a ověření, že navigace probíhá podle očekávané trasy. Mockujte autorizační a deeplink služby pro izolované testování každého scénáře.

Často kladené otázky

Čím se liší Deferred Navigation od Deep Link?

Deferred Navigation je vzor odloženého přechodu, který lze použít v jakémkoli asynchronním scénáři. Deep Link je jedním ze spouštěčů deferred navigation, ale ne jediným. Autorizace a feature flagy také používají odloženou navigaci.

Lze kombinovat deferred a direct navigation?

Ano, většina aplikací používá hybridní přístup. Obrazovka se seznamem produktů (bez asynchronních závislostí) může používat přímou navigaci a obrazovka detailů s načítáním dat — deferred. Rozdělení je určeno architekturou konkrétní obrazovky.

Jak se vyhnout race condition při deferred navigation?

Použijte SharedFlow bez replay na Androidu a combineLatest bez bufferování na iOS. Zrušte předchozí odběry při novém spouštěči. Tím je zaručeno, že se zpracovává pouze poslední navigační událost.

Podporuje Jetpack Compose deferred navigation?

Ano, v Compose se deferred navigation implementuje přihlášením k odběru StateFlow ViewModel a voláním NavController.navigate v LaunchedEffect. Google doporučuje používat Navigation Compose s událostním modelem pro deferred scénáře.

Jak zpracovat deferred navigation při minimalizované aplikaci?

Odložte navigaci, dokud se aplikace znovu nestane aktivní. V Androidu použijte Lifecycle.State.STARTED pro filtrování událostí. V iOS zkontrolujte UIApplication.State v bloku Combine nebo async/await.

Shrnutí

  • Deferred Navigation — vzor, kde se přechod provádí po dokončení asynchronní operace
  • Hlavní scénáře: autorizace, Deeplink, push oznámení, feature flagy
  • StateFlow/SharedFlow v Androidu a Combine/async-await v iOS — nástroje implementace
  • Deferred Navigation na rozdíl od přímé nezpůsobuje závodní stav při asynchronním načítání
  • Navigation Component v Androidu a Coordinator Pattern v iOS — základní architektury
  • Jediný NavController/Coordinator je povinný pro konzistentní navigaci
  • Testování deferred navigation vyžaduje TestDispatcher a mocky pro asynchronní služby

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také