Deferred Navigation — шта је то, принципи и имплементација

Аутор: IT Sectr Објављено: 2026-06-10 Време читања: 9 мин

Deferred Navigation је образац одложене навигације при којем се прелазак на следећи екран дешава након завршетка асинхроне операције, а не непосредно у тренутку акције корисника. Према Android Developers (2024), deferred navigation омогућава избегавање стања трке између навигације и учитавања података, као и поједностављује обраду прелаза из push обавештења и Deeplink-а. Кључна разлика — рута се израчунава након што су сви потребни подаци доступни.

Главно

  • Deferred Navigation — одложена навигација, где се прелаз извршава након завршетка асинхроних операција
  • Директна навигација обрађује прелаз тренутно, deferred — чека податке
  • Типични сценарији: ауторизација, Deeplink, push обавештења, учитавање конфигурације
  • У Android-у се имплементира кроз Navigation Component са повратним позивима и StateFlow
  • У iOS-у се користи Combine или async/await са координатором навигације

Шта је Deferred Navigation

Deferred Navigation је архитектонски образац у којем се навигациона одлука одлаже до тренутка када сви потребни подаци постану доступни. За разлику од директног прелаза, где корисник притиска дугме и одмах стиже на нови екран, deferred navigation раздваја догађај-окидач и стварни прелаз, додајући између њих асинхрону операцију.

Архитектонски, Deferred Navigation се заснива на промени стања: притисак на дугме покреће асинхрони процес, а претплата на његов резултат покреће навигацију. Ово је посебно важно у апликацијама са архитектуром MVVM или MVI, где ViewModel управља стањем, а View (Activity, Fragment, SwiftUI View) се претплаћује на промене и реагује прелазом. Такав приступ уклања директну зависност између UI-ја и навигационе логике.

Према подацима Google I/O 2023, deferred navigation се препоручује за све сценарије где навигација зависи од резултата мрежног захтева, провере ауторизације, учитавања конфигурације или дозвола. Овај образац је такође обавезан при обради Deeplink-а, када апликација прво мора да се покрене, учита корени екран и тек онда пређе на циљану руту.

Када је потребна одложена навигација

Deferred Navigation се примењује у сценаријима где директни прелаз доводи до неисправног стања екрана или грешака при учитавању. Размотримо четири главна случаја када је deferred navigation обавезна.

Ауторизација и аутентификација

Ако корисник кликне на заштићени садржај, апликација прво мора да провери токен приступа. Директна навигација на екран садржаја довешће до празног екрана или грешке 401 ако је токен истекао. Deferred navigation проверава токен и само при успеху прелази на циљани екран. При неуспеху преусмерава на екран за пријаву.

Обрада Deeplink-а

Када се апликација отвори преко спољног линка, прво треба учитати корени екран, обновити стање навигације и тек онда извршити прелаз путем Deeplink-а. Директна навигација на циљани екран без кореног контекста довешће до аномалија: празног стека навигације или сломљеног back stack-а.

Push обавештења са садржајем

При тапу на push обавештење, апликација може бити у три стања: затворена, у позадини или активна. Deferred Navigation одређује стање апликације, учитава потребан садржај и тек онда приказује циљани екран. iOS омогућава обраду овог сценарија путем UNNotificationContentExtension-а.

Динамички feature flag

Ако функционалност екрана управља feature flag са сервера, deferred navigation омогућава да се прво захтева конфигурација и тек онда прикаже екран. Ако је функција искључена, корисник види алтернативни садржај или placeholder уместо празног екрана.

СценаријДиректна навигацијаDeferred Navigation
АуторизацијаПразан екран при истеклом токенуПреусмеравање на пријаву
DeeplinkСломљен back stackИсправан стек навигације
PushУчитавање без контекстаПодаци спремни пре прелаза
Feature flagПриказ недоступне функцијеPlaceholder или алтернатива

Deferred Navigation vs директна навигација

Директна навигација (direct navigation) — традиционални приступ при којем се прелаз извршава одмах као одговор на догађај. Корисник притиска дугме, и UI рутер одмах пребацује екран. Овај приступ је једноставан и предвидив, али ограничен у сценаријима где су потребни подаци са сервера или провера услова.

Deferred Navigation додаје слој у облику асинхроног стања. Догађај корисника покреће операцију, а претплата на резултат управља навигацијом. То повећава сложеност кода, али даје флексибилност: исти окидач може водити на различите екране у зависности од учитаних података.

Избор између два приступа зависи од захтева: ако за приказ екрана нису потребни асинхрони подаци — користите директну навигацију. Ако екран зависи од резултата захтева, ауторизације или спољних услова — deferred navigation је обавезна. Хибридни приступ, где је део прелаза директан а део одложен, најчешћа је пракса у индустријским апликацијама.

Имплементација у Android-у: Navigation Component и ViewModel

Android Jetpack пружа механизме за имплементацију Deferred Navigation на архитектонском нивоу. Основна идеја — ViewModel управља стањем, а Activity или Fragment се претплаћује на промене и покреће навигацију путем NavController-а.

Deferred Navigation са StateFlow

StateFlow у Kotlin корутинама — идеалан алат за deferred navigation. ViewModel ажурира StateFlow са навигационим догађајем, а Activity га посматра и извршава прелаз. Када је догађај обрађен, StateFlow се чисти, спречавајући поновну навигацију.

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 са NavController

У Activity-ју, претплата на навигацију покреће NavController са рутом из ViewModel-а. За спречавање поновне навигације при ротацији екрана користи се омотач NavigationEventWrapper који обрађује догађај само једном. Jetpack Navigation 2.7+ подржава Safe Args за тип-безбедан пренос аргумената.

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

Имплементација у iOS-у: Coordinator Pattern и Combine

iOS нема уграђени Navigation Component аналоган Android Jetpack-у, па програмери имплементирају Deferred Navigation путем Coordinator Pattern-а у комбинацији са Combine или async/await. Coordinator управља стеком екрана и доноси одлуке о навигацији на основу учитаних података.

Coordinator са Combine

Coordinator — објекат који управља навигацијом између ViewController-а. У комбинацији са Combine, ViewModel објављује догађаје путем PassthroughSubject-а, а Coordinator се претплаћује на њих и извршава прелаз. Овакав приступ потпуно одваја UI од навигационе логике и у складу је са Apple-овим препорукама за архитектуру апликација.

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 са async/await

Swift 5.5 је увео structured concurrency, који омогућава имплементацију одложене навигације путем async/await без Combine-а. ViewModel пружа async функцију која враћа руту након учитавања података. Coordinator позива ову функцију у Task-у и извршава прелаз према добијеној рути.

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

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

Типичне грешке и best practices

Deferred Navigation поједностављује обраду асинхроних сценарија, али захтева дисциплинован приступ управљању стањем. Размотримо главне грешке које програмери праве при имплементацији deferred navigation.

Грешка: навигација пре завршетка иницијализације

Најчешћа грешка — покушај извршења deferred navigation пре него што је корени екран потпуно иницијализован и NavController или Coordinator спреман за прелаз. У Android-у ово доводи до IllegalStateException, у iOS-у — до неодређеног стања UI-ја. Решење — уверите се да је животни циклус компоненте у стању STARTED или RESUMED пре покретања навигације.

Грешка: поновна навигација при повратку на екран

Ако StateFlow или Subject не чисти догађај након обраде, при повратку на претходни екран корисник може бити аутоматски преусмерен на исти екран поново. Користите SharedFlow са replay=0 на Android-у или CurrentValueSubject са nil након обраде на iOS-у, како би се навигациони догађај покренуо само једном.

Best Practice: јединствени NavController или Coordinator

Користите један централни компонент за целокупну навигацију у апликацији. Када свако Activity, Fragment или ViewController има сопствени навигациони контролер, deferred navigation између различитих делова апликације постаје хаотична. Јединствени Coordinator олакшава отклањање грешака и тестирање навигационих сценарија.

Best Practice: тестирање сценарија одложене навигације

Deferred Navigation је теже тестирати од директне, јер асинхроне операције уносе фактор времена. Користите TestDispatcher у Android-у (kotlinx-coroutines-test) и XCTestExpectation у iOS-у за симулацију учитавања података и проверу да се навигација извршава према очекиваној рути. Мокајте сервисе ауторизације и дееплинкова за изоловано тестирање сваког сценарија.

Често постављана питања

По чему се Deferred Navigation разликује од Deep Link-а?

Deferred Navigation је образац одложеног прелаза који се може применити у било ком асинхроном сценарију. Deep Link је један од окидача за deferred navigation, али не и једини. Ауторизација и feature flag-ови такође користе одложену навигацију.

Могу ли се комбиновати deferred и direct navigation?

Да, већина апликација користи хибридни приступ. Екран листе производа (без асинхроних зависности) може користити директну навигацију, а екран детаља са учитавањем података — deferred. Подела је одређена архитектуром конкретног екрана.

Како избећи race condition при deferred navigation?

Користите SharedFlow без replay на Android-у и combineLatest без баферисања на iOS-у. Откажите претходне претплате при новом окидачу. То гарантује да се обрађује само последњи навигациони догађај.

Да ли Jetpack Compose подржава deferred navigation?

Да, у Compose-у се deferred navigation имплементира кроз претплату на StateFlow ViewModel-а и позив NavController.navigate у LaunchedEffect-у. Google препоручује коришћење Navigation Compose-а са догађајним моделом за deferred сценарије.

Како обрадити deferred navigation када је апликација сведена?

Одложите навигацију до тренутка када апликација поново постане активна. У Android-у користите Lifecycle.State.STARTED за филтрирање догађаја. У iOS-у проверите UIApplication.State у Combine или async/await блоку.

Закључак

  • Deferred Navigation — образац при којем се прелаз извршава након завршетка асинхроне операције
  • Главни сценарији: ауторизација, Deeplink, push обавештења, feature flag-ови
  • StateFlow/SharedFlow у Android-у и Combine/async-await у iOS-у — алати имплементације
  • Deferred Navigation за разлику од директне не изазива стање трке при асинхроном учитавању
  • Navigation Component у Android-у и Coordinator Pattern у iOS-у — основне архитектуре
  • Јединствени NavController/Coordinator је обавезан за конзистентну навигацију
  • Тестирање deferred navigation захтева TestDispatcher и мокове за асинхроне сервисе

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође