Deferred Navigation — vad är det, principer och implementering

Författare: IT Sectr Publicerad: 2026-06-10 Lästid: 9 min

Deferred Navigation är ett mönster för uppskjuten navigering där övergången till nästa skärm sker efter att en asynkron operation har slutförts, inte direkt i samband med användarens handling. Enligt Android Developers (2024) gör deferred navigation det möjligt att undvika race condition mellan navigering och dataladdning, samt förenklar hanteringen av övergångar från push-notiser och Deeplink. Den viktigaste skillnaden — rutten beräknas efter att alla nödvändiga data är tillgängliga.

Huvudpunkter

  • Deferred Navigation — uppskjuten navigering, där övergången utförs efter att asynkrona operationer slutförts
  • Direkt navigering bearbetar övergången omedelbart, deferred — väntar på data
  • Typiska scenarier: auktorisering, Deeplink, push-notiser, laddning av konfiguration
  • I Android implementeras via Navigation Component med callbacks och StateFlow
  • I iOS används Combine eller async/await med en navigeringskoordinator

Vad är Deferred Navigation

Deferred Navigation är ett arkitekturmönster där navigeringsbeslutet skjuts upp tills alla nödvändiga data är tillgängliga. Till skillnad från en direkt övergång, där användaren trycker på en knapp och omedelbart kommer till en ny skärm, separerar deferred navigation triggerhändelsen och den faktiska övergången genom att lägga till en asynkron operation mellan dem.

Arkitektoniskt bygger Deferred Navigation på tillståndsförändring: att trycka på en knapp initierar en asynkron process och prenumerationen på dess resultat utlöser navigeringen. Detta är särskilt viktigt i applikationer med MVVM- eller MVI-arkitektur, där ViewModel hanterar tillståndet och View (Activity, Fragment, SwiftUI View) prenumererar på förändringar och reagerar med en övergång. Detta tillvägagångssätt eliminerar det direkta beroendet mellan UI och navigeringslogik.

Enligt Google I/O 2023 rekommenderas deferred navigation för alla scenarier där navigeringen beror på resultatet av en nätverksförfrågan, auktoriseringskontroll, laddning av konfiguration eller behörigheter. Mönstret är också obligatoriskt vid bearbetning av Deeplink, när applikationen först måste starta, ladda rot-skärmen och först därefter gå till målrutten.

När behövs uppskjuten navigering

Deferred Navigation tillämpas i scenarier där en direkt övergång leder till ett felaktigt skärmtillstånd eller laddningsfel. Låt oss titta på fyra huvudsakliga fall där deferred navigation är obligatorisk.

Auktorisering och autentisering

Om en användare klickar på skyddat innehåll måste applikationen först kontrollera åtkomsttoken. Direkt navigering till innehållsskärmen leder till en tom skärm eller fel 401 om token har löpt ut. Deferred navigation kontrollerar token och går endast vid framgång till målskärmen. Vid misslyckande omdirigerar den till inloggningsskärmen.

Bearbetning av Deeplink

När applikationen öppnas via en extern länk måste först rot-skärmen laddas, navigeringstillståndet återställas och först därefter utföra övergången via Deeplink. Direkt navigering till målskärmen utan rotkontext leder till anomalier: tom navigeringsstack eller skadad back stack.

Push-notiser med innehåll

Vid tryck på en push-notis kan applikationen vara i tre tillstånd: stängd, i bakgrunden eller aktiv. Deferred Navigation bestämmer applikationens tillstånd, laddar nödvändigt innehåll och visar först därefter målskärmen. iOS tillåter bearbetning av detta scenario via UNNotificationContentExtension.

Dynamisk feature flag

Om skärmens funktionalitet styrs av en feature flag från servern, gör deferred navigation det möjligt att först begära konfigurationen och först därefter visa skärmen. Om funktionen är inaktiverad ser användaren alternativt innehåll eller en placeholder istället för en tom skärm.

ScenarioDirekt navigeringDeferred Navigation
AuktoriseringTom skärm vid utgången tokenOmdirigering till inloggning
DeeplinkSkadad back stackKorrekt navigeringsstack
PushLaddning utan kontextData redo före övergång
Feature flagVisning av otillgänglig funktionPlaceholder eller alternativ

Deferred Navigation vs direkt navigering

Direkt navigering (direct navigation) är ett traditionellt tillvägagångssätt där övergången utförs omedelbart som svar på en händelse. Användaren trycker på en knapp och UI-router byter omedelbart skärm. Detta tillvägagångssätt är enkelt och förutsägbart, men begränsat i scenarier där data från servern eller villkorskontroller behövs.

Deferred Navigation lägger till ett lager i form av ett asynkront tillstånd. Användarens händelse startar en operation och prenumerationen på resultatet styr navigeringen. Detta ökar kodens komplexitet men ger flexibilitet: samma trigger kan leda till olika skärmar beroende på laddade data.

Valet mellan två tillvägagångssätt beror på kraven: om inga asynkrona data behövs för att visa skärmen — använd direkt navigering. Om skärmen beror på resultatet av en förfrågan, auktorisering eller externa förhållanden — är deferred navigation obligatorisk. Ett hybridt tillvägagångssätt, där en del av övergångarna är direkta och en del uppskjutna, är den vanligaste praxisen i produktionsapplikationer.

Implementering i Android: Navigation Component och ViewModel

Android Jetpack tillhandahåller mekanismer för att implementera Deferred Navigation på arkitekturnivå. Huvudidén — ViewModel hanterar tillståndet och Activity eller Fragment prenumererar på förändringar och utlöser navigering via NavController.

Deferred Navigation med StateFlow

StateFlow i Kotlin coroutines — det ideala verktyget för deferred navigation. ViewModel uppdaterar StateFlow med navigeringshändelsen och Activity observerar den och utför övergången. När händelsen har bearbetats rensas StateFlow, vilket förhindrar upprepad navigering.

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 med NavController

I Activity utlöser prenumeration på navigering NavController med rutten från ViewModel. För att förhindra upprepad navigering vid skärmrotation används en wrapper NavigationEventWrapper som bearbetar händelsen endast en gång. Jetpack Navigation 2.7+ stöder Safe Args för typ-säker argumentöverföring.

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

Implementering i iOS: Coordinator Pattern och Combine

iOS har ingen inbyggd Navigation Component som liknar Android Jetpack, så utvecklare implementerar Deferred Navigation via Coordinator Pattern i kombination med Combine eller async/await. Coordinator hanterar skärmstacken och fattar navigeringsbeslut baserat på laddade data.

Coordinator med Combine

Coordinator är ett objekt som hanterar navigering mellan ViewControllers. I kombination med Combine publicerar ViewModel händelser via PassthroughSubject och Coordinator prenumererar på dem och utför övergången. Detta tillvägagångssätt separerar helt UI från navigeringslogik och följer Apples rekommendationer för apparkitektur.

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 med async/await

Swift 5.5 introducerade structured concurrency, vilket gör det möjligt att implementera uppskjuten navigering via async/await utan Combine. ViewModel tillhandahåller en async-funktion som returnerar rutten efter dataladdning. Coordinator anropar denna funktion i Task och utför övergången enligt den mottagna rutten.

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

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

Vanliga misstag och best practices

Deferred Navigation förenklar hanteringen av asynkrona scenarier, men kräver ett disciplinerat förhållningssätt till tillståndshantering. Låt oss titta på de vanligaste misstagen som utvecklare gör vid implementering av deferred navigation.

Misstag: navigering före slutförd initiering

Det vanligaste misstaget — att försöka utföra deferred navigation innan rot-skärmen är fullt initierad och NavController eller Coordinator är redo för övergången. I Android leder detta till IllegalStateException, i iOS — till ett odefinierat UI-tillstånd. Lösning — se till att komponentens livscykel är i tillståndet STARTED eller RESUMED innan navigering startas.

Misstag: upprepad navigering vid återgång till skärm

Om StateFlow eller Subject inte rensar händelsen efter bearbetning, kan användaren vid återgång till föregående skärm automatiskt omdirigeras till samma skärm igen. Använd SharedFlow med replay=0 i Android eller CurrentValueSubject med nil efter bearbetning i iOS, så att navigeringshändelsen bara utlöses en gång.

Best Practice: en enda NavController eller Coordinator

Använd en central komponent för hela navigeringen i applikationen. När varje Activity, Fragment eller ViewController har sin egen navigeringskontrollant blir deferred navigation mellan olika delar av applikationen kaotisk. En enda Coordinator underlättar felsökning och testning av navigeringsscenarier.

Best Practice: testning av uppskjutna navigeringsscenarier

Deferred Navigation är svårare att testa än direkt navigering eftersom asynkrona operationer introducerar en tidsfaktor. Använd TestDispatcher i Android (kotlinx-coroutines-test) och XCTestExpectation i iOS för simulering av dataladdning och kontrollera att navigeringen utförs enligt den förväntade rutten. Mocka auktoriserings- och deeplinktjänster för isolerad testning av varje scenario.

Vanliga frågor

Vad skiljer Deferred Navigation från Deep Link?

Deferred Navigation är ett mönster för uppskjuten övergång som kan tillämpas i vilket asynkront scenario som helst. Deep Link är en av triggers för deferred navigation, men inte den enda. Auktorisering och feature flags använder också uppskjuten navigering.

Kan deferred och direct navigation kombineras?

Ja, de flesta applikationer använder ett hybridt tillvägagångssätt. En produktlistskärm (utan asynkrona beroenden) kan använda direkt navigering, medan en detaljskärm med dataladdning använder deferred. Uppdelningen bestäms av den specifika skärmens arkitektur.

Hur undviker man race condition vid deferred navigation?

Använd SharedFlow utan replay i Android och combineLatest utan buffring i iOS. Avbryt tidigare prenumerationer vid ny trigger. Detta garanterar att endast den senaste navigeringshändelsen bearbetas.

Stöder Jetpack Compose deferred navigation?

Ja, i Compose implementeras deferred navigation genom prenumeration på ViewModels StateFlow och anrop av NavController.navigate i LaunchedEffect. Google rekommenderar att använda Navigation Compose med en händelsemodell för deferred-scenarier.

Hur hanterar man deferred navigation när appen är minimerad?

Skjut upp navigeringen tills appen blir aktiv igen. I Android använder du Lifecycle.State.STARTED för att filtrera händelser. I iOS kontrollerar du UIApplication.State i Combine- eller async/await-blocket.

Sammanfattning

  • Deferred Navigation — mönster där övergången utförs efter att en asynkron operation slutförts
  • Huvudscenarier: auktorisering, Deeplink, push-notiser, feature flags
  • StateFlow/SharedFlow i Android och Combine/async-await i iOS — implementeringsverktyg
  • Deferred Navigation till skillnad från direkt orsakar inte race condition vid asynkron laddning
  • Navigation Component i Android och Coordinator Pattern i iOS — basarkitekturer
  • En enda NavController/Coordinator är obligatorisk för konsekvent navigering
  • Testning av deferred navigation kräver TestDispatcher och mockar för asynkrona tjänster

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också