An open API service indexing awesome lists of open source software.

https://github.com/silkodenis/swiftui-navigation-coordinator

A lightweight SwiftUI Navigation Coordinator package that supports stack navigation and modal presentation.
https://github.com/silkodenis/swiftui-navigation-coordinator

coordinator dismiss example ios modal navigation pattern pop present push segue stack unwind unwind-segue xcodeproj

Last synced: 3 months ago
JSON representation

A lightweight SwiftUI Navigation Coordinator package that supports stack navigation and modal presentation.

Awesome Lists containing this project

README

        

[![License](https://img.shields.io/github/license/silkodenis/swiftui-navigation-coordinator.svg)](https://github.com/silkodenis/swiftui-navigation-coordinator/blob/main/LICENSE)
![swift](https://github.com/silkodenis/swiftui-navigation-coordinator/actions/workflows/swift.yml/badge.svg?branch=main)

# SwiftUI Navigation Coordinator


Screenshot 1
Screenshot 2
Screenshot 3
Screenshot 4

## About the Project
This project provides a lightweight Navigation Coordinator, using SwiftUI NavigationStack (available from iOS 16).

## Core Features
The current implementation covers 6 main transitions:

Stack Navigation:

- `push` — navigates forward to a new view.
- `pop` — returns to the previous view.
- `unwind` — performs a multi-level return.
- `popToRoot` — returns to the root view.

Modal Presentation:

- `present` — displays a modal view, overlaying it on top of current content.
- `dismiss` — closes the current modal view and returns to the underlying content.

## Requirements

- **iOS**: iOS 16.0+
- **macOS**: macOS 13.0+
- **watchOS**: watchOS 9.0+
- **tvOS**: tvOS 16.0+

## Add with Swift Package Manager:

1. Open Xcode and select “File” > “Add Packages…”
2. Enter the URL of the package repository.
3. Follow the instructions to complete the installation.

## Getting Started

1. Configure the NavigableScreen Enum

Start by creating an enum Screen to represent the different screens in your app. Ensure it conforms to the NavigableScreen protocol:

```swift
import NavigationCoordinator

enum Screen {
case login
case movies
case settings
// etc.
}

extension Screen: NavigableScreen {
@ViewBuilder
var view: some View {
switch self {
case .login: LoginView()
case .movies: MoviesView()
case .settings: SettingsView()
}
}
}
```

2. Define Typealiases

Define typealias to simplify the usage of the types used with your coordinator:

```swift
import NavigationCoordinator

typealias SegueModifier = RegisterSegueModifier
typealias Coordinator = NavigationCoordinator
typealias RootView = NavigationStackRootView
```

3. Configure the App Entry Point

Set up the app entry point using the RootView to define the initial screen:

```swift
import SwiftUI

@main
struct MainApp: App {
var body: some Scene {
WindowGroup {
RootView(.login)
}
}
}
```

## Usage Examples

Push

```swift
import SwiftUI

struct LoginView: View {
@EnvironmentObject var coordinator: Coordinator

var body: some View {
Button("Movies") {
coordinator.push(.movies)
}
}
}
```

Pop

```swift
import SwiftUI

struct MoviesView: View {
@EnvironmentObject var coordinator: Coordinator

var body: some View {
Button("back") {
coordinator.pop()
}
}
}
```

PopToRoot

```swift
import SwiftUI

struct SettingsView: View {
@EnvironmentObject var coordinator: Coordinator

var body: some View {
Button("login") {
coordinator.popToRoot()
}
}
}
```

Unwind
Use a unique identifier for your unwind segues. If a segue becomes no longer relevant, it will be automatically removed from the coordinator. Using `onUnwind()` modifier is completely safe, tested, and does not involve any memory leaks or unintended calls.

```swift
import SwiftUI

// B View
// 🟦🟦🅰🟦🟦🟦🟦🟦🟦🅱️
struct B: View {
@EnvironmentObject var coordinator: Coordinator

var body: some View {
Button("pop to A") {
coordinator.unwind(to: "identifier" /*, with: Any?*/)
}
}
}

// A View
// 🟦🟦🅰️
struct A: View {
var body: some View {
VStack {}
.onUnwind(segue: "identifier") /*{ Any? in }*/
}
}
```
`onUnwind()` will always be called before `onAppear()`.

Present

```swift
import SwiftUI

/*
[B]
[ ][ ][ ][ ][ ][A]
*/
struct A: View {
@EnvironmentObject var coordinator: Coordinator

var body: some View {
Button("present") {
coordinator.present(.B)
}
}
}
```

Dismiss

```swift
import SwiftUI

/*
[B][ ][ ][ ][CL]
[ ][ ][ ][ ][ ][A]
*/
struct CL: View {
@EnvironmentObject var coordinator: Coordinator

var body: some View {
Button("dismiss") {
coordinator.dismiss(/*to: "identifier" /*, with: Any?*/*/)
}
}
}

/*
[ ][ ][ ][ ][ ][A]
*/
struct A: View {
@EnvironmentObject var coordinator: Coordinator

var body: some View {
VStack {}
// Not necessary. Only if you need to capture an onDismiss event.
.onDismiss(segue: "identifier") /*{ Any? in }*/
}
}

```

## Project examples
- [Original Example](https://github.com/silkodenis/swiftui-navigation-coordinator/tree/main/Example)
- [SwiftUI Redux TMDB Demo App](https://github.com/silkodenis/swiftui-moviesdb-redux-app)

## Reporting Issues

I welcome any issues you find within the project. If you encounter bugs or have suggestions for improvements, please feel free to create an issue on the GitHub repository.

## License

**Apache License 2.0**. See the [LICENSE](https://github.com/silkodenis/swiftui-navigation-coordinator/blob/main/LICENSE) file for details.