Contents

ahmdmhasn/swiftui-imperative-navigation

SwiftUI Imperative Navigation Example

✨ Features

  • 🎯 Imperative Navigation API - Control navigation programmatically from coordinators or view models
  • πŸ“± Multiple Presentation Styles - Support for push, sheet, and full-screen cover presentations
  • πŸ—οΈ Coordinator Pattern - Decouple navigation logic from views for better architecture
  • βœ… Type-Safe - Compile-time safety for navigation parameters
  • πŸ§ͺ Testable - Mock coordinators and test navigation flows easily
  • πŸ”„ State Management - Share state across navigation flows seamlessly
  • πŸ“¦ Lightweight - Minimal dependencies, just SwiftUI

πŸ“Œ Why Imperative Navigation?

While SwiftUI promotes declarative navigation with NavigationStack and NavigationLink, complex navigation flows can become challenging to manage. Imperative navigation offers:

  • βœ… Separation of Concerns - Navigation logic lives outside of views
  • βœ… Enhanced Testability - Test navigation flows independently from UI
  • βœ… Complex Flows Made Simple - Handle multi-step processes, conditional navigation, and dynamic routing
  • βœ… Single Source of Truth - Centralized navigation state management
  • βœ… Better Code Organization - Clear responsibility boundaries between views and navigation

🏞️ Screenshot

<img src="Screenshots/Sample.gif"/>

πŸ“¦ Installation

Swift Package Manager

Add the package to your project using Xcode:

  1. File β†’ Add Package Dependencies
  2. Enter the repository URL:

`` https://github.com/ahmdmhasn/swiftui-imperative-navigation.git ``

  1. Select the version and add to your target

Or add it to your Package.swift:

dependencies: [
    .package(url: "https://github.com/ahmdmhasn/swiftui-imperative-navigation.git", from: "1.0.0")
]

Requirements

  • iOS 16.0+ / watchOS 9.0+ / tvOS 16.0+ / visionOS 1.0+
  • Xcode 16.0+
  • Swift 6.0+

πŸš€ Quick Start

1. Create a Navigation Controller

@MainActor
final class AppCoordinator {
    let navigationController = NavigationController()

    func showDetail(_ item: Item) {
        navigationController.push(DetailView(item: item))
    }

    func showSettings() {
        navigationController.sheet(SettingsView())
    }

    func showConfirmation() {
        navigationController.present(ConfirmationView())
    }
}

2. Set Up Your Root View

@main
struct MyApp: App {
    @StateObject private var coordinator = AppCoordinator()

    var body: some Scene {
        WindowGroup {
            NavigationView(
                controller: coordinator.navigationController,
                root: {
                    HomeView(coordinator: coordinator)
                }
            )
        }
    }
}

3. Navigate From Your Views

struct HomeView: View {
    let coordinator: AppCoordinator

    var body: some View {
        VStack {
            Button("Show Detail") {
                coordinator.showDetail(selectedItem)
            }

            Button("Show Settings") {
                coordinator.showSettings()
            }
        }
    }
}

πŸ“– API Reference

NavigationController

The central controller for managing navigation:

@MainActor
public final class NavigationController: ObservableObject {
    // Push a view onto the navigation stack
    public func push<V: View>(_ view: V)

    // Pop the top view from the stack
    public func pop()

    // Pop all views and return to root
    public func popToRoot()

    // Present a view as a full-screen cover
    public func present<V: View>(_ view: V)

    // Present a view as a sheet modal
    public func sheet<V: View>(_ view: V)

    // Dismiss the current modal (sheet or full-screen)
    public func dismiss()
}

πŸ’‘ Example App

Check out the comprehensive example app included in the repository. It demonstrates:

  • πŸ›’ E-commerce shopping flow with product catalog
  • πŸ“¦ Product details with reviews and ratings
  • πŸ›οΈ Shopping cart with sheet presentation
  • πŸ’³ Multi-step checkout process
  • βœ… Order confirmation with full-screen cover
  • πŸ”„ Complex navigation flows and state management

View the example code β†’

πŸ§ͺ Testing

The package includes comprehensive unit tests for the navigation controller. The coordinator pattern makes your navigation logic highly testable:

@MainActor
final class MockAppCoordinator: AppCoordinator {
    var didShowDetail = false
    var didShowSettings = false

    override func showDetail(_ item: Item) {
        didShowDetail = true
    }

    override func showSettings() {
        didShowSettings = true
    }
}

// Test your view models or coordinators
func testNavigation() {
    let coordinator = MockCoordinator()
    coordinator.showDetail(item)
    XCTAssertTrue(coordinator.didShowDetail)
}

🀝 Contributing

Contributions are welcome! Here's how you can help:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Please read our contribution guidelines before submitting a PR.

πŸ“œ License

This project is licensed under the MIT License. See the LICENSE file for details.

πŸ‘¨β€πŸ’» Author

Ahmed M. Hassan

🌟 Support

If you find this project helpful:

  • ⭐ Star the repository
  • 🐦 Share on social media
  • πŸ“ Write about it
  • πŸ’¬ Provide feedback and suggestions

πŸ™ Acknowledgments

Thanks to the SwiftUI community for inspiration and feedback on navigation patterns.

Package Metadata

Repository: ahmdmhasn/swiftui-imperative-navigation

Stars: 6

Forks: 0

Open issues: 1

Default branch: main

Primary language: swift

License: MIT

README: Readme.md