{"id":13752066,"url":"https://github.com/johnpatrickmorgan/FlowStacks","last_synced_at":"2025-05-09T18:33:02.178Z","repository":{"id":36975627,"uuid":"377967286","full_name":"johnpatrickmorgan/FlowStacks","owner":"johnpatrickmorgan","description":"FlowStacks allows you to hoist SwiftUI navigation and presentation state into a Coordinator","archived":false,"fork":false,"pushed_at":"2024-11-24T23:17:31.000Z","size":245,"stargazers_count":892,"open_issues_count":15,"forks_count":69,"subscribers_count":14,"default_branch":"main","last_synced_at":"2025-05-06T02:22:05.464Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/johnpatrickmorgan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-06-17T21:43:45.000Z","updated_at":"2025-05-03T04:21:04.000Z","dependencies_parsed_at":"2023-07-13T21:21:15.381Z","dependency_job_id":"efbc7dfd-d9d6-4cbe-b309-71dabfa2ed24","html_url":"https://github.com/johnpatrickmorgan/FlowStacks","commit_stats":{"total_commits":139,"total_committers":9,"mean_commits":"15.444444444444445","dds":0.1079136690647482,"last_synced_commit":"ffb40788e7ef62b241da7e6b4cbe69e0d39524d0"},"previous_names":["johnpatrickmorgan/nstack"],"tags_count":51,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnpatrickmorgan%2FFlowStacks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnpatrickmorgan%2FFlowStacks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnpatrickmorgan%2FFlowStacks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnpatrickmorgan%2FFlowStacks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/johnpatrickmorgan","download_url":"https://codeload.github.com/johnpatrickmorgan/FlowStacks/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253303205,"owners_count":21886907,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T09:00:58.958Z","updated_at":"2025-05-09T18:33:00.173Z","avatar_url":"https://github.com/johnpatrickmorgan.png","language":"Swift","readme":"# FlowStacks\n\nThis package takes SwiftUI's familiar and powerful `NavigationStack` API and gives it superpowers, allowing you to use the same API not just for push navigation, but also for presenting sheets and full-screen covers. And because it's implemented using the navigation APIs available in older SwiftUI versions, you can even use it on earlier versions of iOS, tvOS, watchOS and macOS.\n\nYou might like this library if:\n\n✅ You want to support deeplinks into deeply nested navigation routes in your app.\u003cbr/\u003e\n✅ You want to easily re-use views within different navigation contexts.\u003cbr/\u003e\n✅ You want to easily go back to the root screen or a specific screen in the navigation stack.\u003cbr/\u003e\n✅ You want to use the coordinator pattern to keep navigation logic in a single place.\u003cbr/\u003e\n✅ You want to break an app's navigation into multiple reusable coordinators and compose them together.\u003cbr/\u003e\n\n### Familiar APIs\n\nIf you already know SwiftUI's `NavigationStack` APIs, `FlowStacks` should feel familiar and intuitive. Just replace 'Navigation' with 'Flow' in type and function names:\n \n✅ `NavigationStack` -\u003e `FlowStack`\n\n✅ `NavigationLink` -\u003e `FlowLink`\n\n✅ `NavigationPath` -\u003e `FlowPath`\n\n✅ `navigationDestination` -\u003e `flowDestination`\n\n`NavigationStack`'s full API is replicated, so you can initialise a `FlowStack` with a binding to an `Array`, with a binding to a `FlowPath`, or with no binding at all. The only difference is that the array should be a `[Route\u003cMyScreen\u003e]`s instead of `[MyScreen]`. The `Route` enum combines the destination data with info about what style of presentation is used. Similarly, when you create a `FlowLink`, you must additionally specify the route style, e.g. `.push`, `.sheet` or `.cover`. As with `NavigationStack`, if the user taps the back button or swipes to dismiss a sheet, the routes array will be automatically updated to reflect the new navigation state. \n\n## Example\n\n\u003cdetails\u003e\n  \u003csummary\u003eClick to expand an example\u003c/summary\u003e\n\n```swift\nimport FlowStacks\nimport SwiftUI\n\nstruct ContentView: View {\n  @State var path = FlowPath()\n  @State var isShowingWelcome = false\n\n  var body: some View {\n    FlowStack($path, withNavigation: true) {\n      HomeView()\n        .flowDestination(for: Int.self, destination: { number in\n          NumberView(number: number)\n        })\n        .flowDestination(for: String.self, destination: { text in\n          Text(text)\n        })\n        .flowDestination(isPresented: $isShowingWelcome, style: .sheet) {\n          Text(\"Welcome to FlowStacks!\")\n        }\n    }\n  }\n}\n\nstruct HomeView: View {\n  @EnvironmentObject var navigator: FlowPathNavigator\n  \n  var body: some View {\n    List {\n      ForEach(0 ..\u003c 10, id: \\.self) { number in\n        FlowLink(value: number, style: .sheet(withNavigation: true), label: { Text(\"Show \\(number)\") })\n      }\n      Button(\"Show 'hello'\") {\n        navigator.push(\"Hello\")\n      }\n    }\n    .navigationTitle(\"Home\")\n  }\n}\n\nstruct NumberView: View {\n  @EnvironmentObject var navigator: FlowPathNavigator\n  let number: Int\n\n  var body: some View {\n    VStack(spacing: 8) {\n      Text(\"\\(number)\")\n      FlowLink(\n        value: number + 1,\n        style: .push,\n        label: { Text(\"Show next number\") }\n      )\n      Button(\"Go back to root\") {\n        navigator.goBackToRoot()\n      }\n    }\n    .navigationTitle(\"\\(number)\")\n  }\n}\n```\n\n\u003c/details\u003e\n\n## Additional features\n\nAs well as replicating the standard features of the new `NavigationStack` APIs, some helpful utilities have also been added. \n\n### FlowNavigator\n\nA `FlowNavigator` object is available through the environment, giving access to the current routes array and the ability to update it via a number of convenience methods. The navigator can be accessed via the environment, e.g. for a `FlowPath`-backed stack:\n\n```swift\n@EnvironmentObject var navigator: FlowPathNavigator\n```\n\nOr for a FlowStack backed by a routes array, e.g. `[Route\u003cScreenType\u003e]`:\n\n```swift\n@EnvironmentObject var navigator: FlowNavigator\u003cScreenType\u003e\n```\n\nHere's an example of a `FlowNavigator` in use:\n\n```swift\n@EnvironmentObject var navigator: FlowNavigator\u003cScreenType\u003e\n\nvar body: some View {\n  VStack {\n    Button(\"View detail\") {\n      navigator.push(.detail)\n    }\n    Button(\"Go back to profile\") {\n      navigator.goBackTo(.profile)\n    }\n    Button(\"Go back to root\") {\n      navigator.goBackToRoot()\n    }\n  }\n}\n```\n\n### Convenience methods\n\nWhen interacting with a `FlowNavigator` (and also the original `FlowPath` or routes array), a number of convenience methods are available for easier navigation, including:\n\n| Method       | Effect                                            |\n|--------------|---------------------------------------------------|\n| push         | Pushes a new screen onto the stack.               |\n| presentSheet | Presents a new screen as a sheet.†                |\n| presentCover | Presents a new screen as a full-screen cover.†    |\n| goBack       | Goes back one screen in the stack.                |\n| goBackToRoot | Goes back to the very first screen in the stack.  |\n| goBackTo     | Goes back to a specific screen in the stack.      |\n| pop          | Pops the current screen if it was pushed.         |\n| dismiss      | Dismisses the most recently presented screen.     |\n\n_† Pass `embedInNavigationView: true` if you want to be able to push screens from the presented screen._\n\n### Deep-linking\n \n Before the `NavigationStack` APIs were introduced, SwiftUI did not support pushing more than one screen in a single state update, e.g. when deep-linking to a screen multiple layers deep in a navigation hierarchy. *FlowStacks* works around this limitation: you can make any such changes, and the library will, behind the scenes, break down the larger update into a series of smaller updates that SwiftUI supports, with delays if necessary in between.\n\n### Bindings\n\nThe flow destination can be configured to work with a binding to its screen state in the routes array, rather than just a read-only value - just add `$` before the screen argument in the `flowDestination` function's view-builder closure. The screen itself can then be responsible for updating its state within the routes array, e.g.:\n\n```swift\nimport SwiftUINavigation\n\nstruct BindingExampleCoordinator: View {\n  @State var path = FlowPath()\n    \n  var body: some View {\n    FlowStack($path, withNavigation: true) {\n      FlowLink(value: 1, style: .push, label: { Text(\"Push '1'\") })\n        .flowDestination(for: Int.self) { $number in\n          EditNumberScreen(number: $number) // This screen can now change the number stored in the path.\n        }\n    }\n  }\n```\n\n If you're using a typed Array of routes, you're probably using an enum to represent the screen, so it might be necessary to further extract the associated value for a particular case of that enum as a binding. You can do that using the [SwiftUINavigation](https://github.com/pointfreeco/swiftui-navigation) library, which includes a number of helpful Binding transformations for optional and enum state, e.g.:\n\n\n\n\u003cdetails\u003e\n  \u003csummary\u003eClick to expand an example of using a Binding to a value in a typed Array of enum-based routes\u003c/summary\u003e\n  \n```swift\nimport FlowStacks\nimport SwiftUI\nimport SwiftUINavigation\n\nenum Screen: Hashable {\n  case number(Int)\n  case greeting(String)\n}\n\nstruct BindingExampleCoordinator: View {\n  @State var routes: Routes\u003cScreen\u003e = []\n\n  var body: some View {\n    FlowStack($routes, withNavigation: true) {\n      HomeView()\n        .flowDestination(for: Screen.self) { $screen in\n          if let number = Binding(unwrapping: $screen, case: /Screen.number) {\n            // Here `number` is a `Binding\u003cInt\u003e`, so `EditNumberScreen` can change its\n            // value in the routes array.\n            EditNumberScreen(number: number)\n          } else if case let .greeting(greetingText) = screen {\n            // Here `greetingText` is a plain `String`, as a binding is not needed.\n            Text(greetingText)\n          }\n        }\n    }\n  }\n}\n\nstruct HomeView: View {\n  @EnvironmentObject var navigator: FlowPathNavigator\n\n  var body: some View {\n    VStack {\n      FlowLink(value: Screen.number(42), style: .push, label: { Text(\"Show Number\") })\n      FlowLink(value: Screen.greeting(\"Hello world\"), style: .push, label: { Text(\"Show Greeting\") })\n    }\n  }\n}\n\nstruct EditNumberScreen: View {\n  @Binding var number: Int\n\n  var body: some View {\n    Stepper(\n      label: { Text(\"\\(number)\") },\n      onIncrement: { number += 1 },\n      onDecrement: { number -= 1 }\n    )\n  }\n}\n\n```\n\u003c/details\u003e\n\n### Child flow coordinators\n\n`FlowStack`s are designed to be composable, so that you can have multiple flow coordinators, each with its own `FlowStack`, and you can present or push a child coordinator from a parent. See [Nesting FlowStacks](Docs/Nesting%20FlowStacks.md) for more info.\n\n## How does it work? \n\nThe library works by translating the array of routes into a hierarchy of nested NavigationLinks and presentation calls, expanding on the technique used in [NavigationBackport](https://github.com/johnpatrickmorgan/NavigationBackport).\n\n## Migrating from earlier versions\n\nPlease see the [migration docs](Docs/Migration/Migrating%20to%201.0.md).\n\n-------\n\n[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/T6T114GWOT)\n\n","funding_links":["https://ko-fi.com/T6T114GWOT"],"categories":["Uncategorized","Navigation","Swift","🌎 by the community"],"sub_categories":["Uncategorized","Content"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnpatrickmorgan%2FFlowStacks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjohnpatrickmorgan%2FFlowStacks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnpatrickmorgan%2FFlowStacks/lists"}