Router — Routing Pattern Basics in iOS and Android

Author: IT Sectr Published: 2026-02-19 Reading time: 9 min

Router — an architectural pattern that centralizes navigation logic between application screens. Router determines where and how to navigate when an event occurs. In iOS, Router abstracts UINavigationController and segue; in Android — FragmentManager and NavController from Jetpack Navigation. Router can work as a standalone pattern or as a component of Coordinator (Router-per-Coordinator) and VIPER (Router-per-module). More details — in Android Navigation Component Guide.

Key Takeaways

  • Router — routing pattern that centralizes navigation between screens
  • Two levels — Router can be a Coordinator component or a standalone pattern
  • iOS Router — abstraction over UINavigationController with push/present/pop methods
  • Android Router — Jetpack Navigation Component with NavController and deep links
  • URL Routing — routing based on URL schemes (deeplink, universal link)

What is Router: The Essence of the Routing Pattern

Router — a pattern that encapsulates the logic of transitioning between screens. The main idea: ViewController (or Presenter/ViewModel) does not call navigationController.pushViewController directly, but notifies the Router of the intent to navigate. The Router decides which ViewController to create and how to display it. Router can be simple (a set of navigation methods) or complex (with URL scheme routing, deep links, and push notifications).

Three approaches to Router — Router as a service (global Navigator singleton), Router as a module component (VIPER approach), Router as a protocol (abstraction for Coordinator). Global Router — simplicity (one instance per app) — popular in small projects. Modular Router — each feature has its own Router — VIPER and Clean Architecture standard. Protocol Router — used in Coordinator: RouterProtocol with push/present/pop/dismiss methods.

ApproachStructureWhen to Use
Global NavigatorSingleton with navigation methodsSmall projects, prototypes
Modular RouterRouter inside each moduleVIPER, Clean Architecture
Protocol RouterProtocol + implementation for CoordinatorMVVM-C, Coordinator pattern

History of the pattern — Router appeared in web frameworks (URL Router — Rails Routes, Express.js) and was adapted for mobile development. In iOS, Router became popular with the rise of VIPER (2015-2017). In Android, Router is part of Jetpack Navigation (2018), Google's standard navigation solution. Today, Router is an integral part of any modular mobile application architecture.

Router in iOS: Swift Implementation

iOS Router — a protocol with methods for various navigation types. The basic implementation wraps UINavigationController. Router can handle modal presentations (present/dismiss), push/pop in the navigation stack, showDetail for Split View Controller, and full-screen presentations (iOS 13+). Router does not create ViewControllers — it receives a ready instance and displays it. ViewController creation is handled by a factory (Assembly, DI container) or Coordinator.

swift
// RouterProtocol for iOS
protocol RouterProtocol {
    func push(_ viewController: UIViewController, animated: Bool)
    func pop(animated: Bool)
    func popToRoot(animated: Bool)
    func present(_ viewController: UIViewController, animated: Bool)
    func dismiss(animated: Bool)
    func setViewControllers(_: [UIViewController], animated: Bool)
}

// Router implementation via UINavigationController
class NavigationRouter: RouterProtocol {
    private let navigationController: UINavigationController

    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }

    func push(_ vc: UIViewController, animated: Bool) {
        if navigationController.presentedViewController {
            navigationController.dismiss(animated: false)
        }
        navigationController.pushViewController(vc, animated: animated)
    }

    func pop(animated: Bool) {
        navigationController.popViewController(animated: animated)
    }

    func popToRoot(animated: Bool) {
        navigationController.popToRootViewController(animated: animated)
    }

    func present(_ vc: UIViewController, animated: Bool) {
        navigationController.present(vc, animated: animated)
    }

    func dismiss(animated: Bool) {
        navigationController.dismiss(animated: animated)
    }

    func setViewControllers(_ vcs: [UIViewController], animated: Bool) {
        navigationController.setViewControllers(vcs, animated: animated)
    }
}

// Assembly — creating a module with Router
protocol ProfileModuleFactory {
    func makeProfileView() -> UIViewController
}

Router in VIPER — each VIPER module has its own Router, which knows which screens to open from the current module. The Router creates the next module's ViewController through a factory and passes control. Router in VIPER is the only component that contains import UIKit (except View). This simplifies testing of Presenter and Interactor: they do not depend on UIKit.

Router in Android: Jetpack Navigation Component

Jetpack Navigation Component — the standard router for Android, recommended by Google since 2018. Navigation is defined in an XML graph (nav_graph.xml): nodes (fragments, activities, destinations) and edges (actions). NavController manages transitions: calls FragmentManager.replace/add and manages the back stack. Navigation Component supports deep links, arguments, transition animations, and Safe Args for type-safe data passing.

kotlin
// nav_graph.xml — navigation graph
<!-- @xml/nav_graph.xml -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_graph"
    app:startDestination="@id/loginFragment">

    <fragment android:id="@+id/loginFragment"
        android:name=".ui.login.LoginFragment">
        <action android:id="@+id/to_home"
            app:destination="@id/homeFragment" />
    </fragment>

    <fragment android:id="@+id/homeFragment"
        android:name=".ui.home.HomeFragment">
        <argument android:name="userId"
            android:defaultValue="0"
            app:argType="integer" />
        <deepLink app:uri="myapp://home/{userId}" />
    </fragment>
</navigation>

// Kotlin — navigation via NavController
class LoginFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.loginButton.setOnClickListener {
            val action = LoginFragmentDirections.toHome(userId = 42)
            findNavController().navigate(action)
        }
    }
}

// Safe Args — type-safe arguments
class HomeFragmentArgs : NavArgs {
    val userId: Int get() = arguments?.getInt("userId") ?: 0
}

// NavigationUI — integration with Toolbar, BottomNav, Drawer
class MainActivity : AppCompatActivity() {
    private lateinit var navController: NavController

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val navHost = supportFragmentManager
            .findFragmentById(R.id.nav_host_fragment) as NavHostFragment
        navController = navHost.navController
        NavigationUI.setupActionBarWithNavController(this, navController)
    }
}

Deep links in Android Navigation — Navigation Component supports explicit deep links (PendingIntent + NavDeepLinkBuilder) and implicit ones (through intent-filter in the manifest). A deep link can lead to any nav_graph node. Push notification handling: Notification creates a PendingIntent with NavDeepLinkBuilder, which restores the back stack and opens the desired screen. Navigation Component automatically handles Up/Back buttons and returns the user to the previous screen.

URL Routing: Deeplink and Universal Link Routing

URL Routing — routing based on URL schemes, deep links, and universal links. The app registers a scheme (myapp://profile/42) or universal link (https://example.com/profile/42). The Router parses the URL, extracts parameters (userId=42), and opens the corresponding screen. URL Routing allows opening the app from outside: via a link from a website, email, push notification, QR code. iOS uses NSUserActivity + universal link, Android — intent-filter + deep link.

swift
// URL Router — deep link parsing and navigation
protocol URLRoute {
    var pattern: String { get }
    func navigate(parameters: [String: String], router: RouterProtocol)
}

struct ProfileRoute: URLRoute {
    let pattern = "myapp://profile/{userId}"

    func navigate(parameters: [String: String], router: RouterProtocol) {
        guard let userId = parameters["userId"] else { return }
        let profileVC = ProfileViewController(userId: userId)
        router.push(profileVC, animated: true)
    }
}

// URL Router — mapping URLs to routes
class URLRouter {
    private var routes: [URLRoute] = []

    func handle(_ url: URL, router: RouterProtocol) {
        for route in routes {
            if let params = matchPattern(route.pattern, url: url) {
                route.navigate(parameters: params, router: router)
                return
            }
        }
    }
}

Deep link routing in practice — the app must handle deep links in three states: not running (launch with deep link), in background (restore from background), active (onNewIntent/SceneDelegate). The URL Router must correctly restore the back stack: when deep linking to a profile screen, the user should be able to press Back and return to the previous screen. iOS and Android handle back stacks differently — the Router must account for platform-specific features.

Comparing Router with Coordinator and Navigator

Router vs Coordinator — Router answers the question "how to show a screen?" (push/present), Coordinator — "which screen to show?" (which flow to start). Router is a navigation tool, Coordinator is a flow organizer. Router is used by Coordinator. In VIPER, Router performs both functions: decides where to go and how to show it. In MVVM-C, Router is a separate protocol used by Coordinator. The patterns do not exclude but complement each other.

CharacteristicRouterCoordinatorNavigator
FocusNavigation mechanicsNavigation flowGlobal access
LevelUI framework (push/present)Business logic (flow)Infrastructure
Screen creationReceives ready VCCreates VC via factoryCreates VC
LifecycleOne per navigationchildCoordinatorsSingleton

Router vs Navigator — Navigator is an older name for Router in the iOS community. In early implementations, Navigator was a Singleton class with navigation methods. Navigator is a global Router singleton. Navigator is simpler but creates hidden dependencies. Protocol-based Router is more testable and modular. Navigator suits projects without strict architecture, Router — for Clean Architecture and VIPER.

Frequently Asked Questions

What is the difference between Router and Coordinator?

Router is an abstraction over UINavigationController (push/present/pop). Coordinator is a layer above Router that manages flow (which screen to show next). Router handles the mechanics, Coordinator handles the navigation logic. Coordinator uses Router to perform transitions. In VIPER, Router combines both functions; in MVVM-C, they are separated.

Is it mandatory to use Router in a mobile app?

No. For apps with 3-5 screens, direct calls to navigationController.pushViewController are a perfectly fine solution. Router becomes useful with 10+ screens, deep links, push notifications, and modular architecture. Router simplifies navigation testing and allows centralized handling of transitions from any source (button, deep link, push notification).

How is Router related to Deep Links?

URL Router is an extension of Router that parses deep links and universal links. URL Router determines the mapping between URLs and app screens. For example, route myapp://profile/42 → ProfileViewController(userId: 42). URL Router centralizes deep link handling from different sources: push notifications, QR codes, email links, universal links.

Can Router be used without Coordinator?

Yes. Router can work independently — as a global Navigator or as a VIPER module component. Router without Coordinator is simply encapsulating UINavigationController behind a protocol. Coordinator without Router is more complex (ViewController calls push/present directly), but possible. Best practice is Router + Coordinator together.

Is Router in Android the same as iOS Router?

Functionally — yes, both centralize navigation. Implementation is different: iOS Router — protocol + class around UINavigationController; Android — Jetpack Navigation Component with XML graph and NavController. Android Router is built into the framework (Navigation Component), iOS Router is a third-party implementation. Android Router supports Safe Args, iOS Router does not (needs generics or Codable).

Summary

  • Router — routing pattern that encapsulates transition logic between screens
  • iOS Router — protocol with push/present/pop methods around UINavigationController
  • Android Router — Jetpack Navigation Component with XML nav_graph and NavController
  • URL Routing — routing via deep links and universal links
  • Router vs Coordinator — Router handles mechanics, Coordinator handles navigation flow
  • Application — modular architecture, deep links, navigation testing

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also