Navigation helpers for SwiftUI applications build with ComposableArchitecture

Overview

Swift Composable Presentation

Swift v5.4 platforms iOS

📝 Description

Navigation helpers for SwiftUI applications build with ComposableArchitecture.

More info about the concept can be found in the article: Thoughts on SwiftUI navigation.

🏛 Project structure

ComposablePresentation (Xcode Workspace)
 ├─ swift-composable-presentation (Swift Package)
 |   └─ ComposablePresentation (Library)
 ├─ Example (Xcode Project)
 |   └─ Example (iOS Application)
 └─ Tests (Xcode Test Plan)

▶️ Usage

  • Check out the included example app:
    • Open ComposablePresentation.xcworkspace in Xcode.
    • Example source code is contained in Example Xcode project.
    • Run the app using Example build scheme.
  • Add as a dependency to your project:

🛠 Develop

  • Use Xcode ≥ 12.5.
  • Clone the repository or create a fork & clone it.
  • Open ComposablePresentation.xcworkspace in Xcode
  • Use ComposablePresentation scheme for building the library and running unit tests.
  • If you want to contribute:
    • Create a pull request containing your changes or bugfixes.
    • Make sure to add tests for the new/updated code.

☕️ Do you like the project?

Buy Me A Coffee

📄 License

Copyright © 2021 Dariusz Rybicki Darrarski

License: MIT

Comments
  • Initial App State with routes populated isn't rendered correctly

    Initial App State with routes populated isn't rendered correctly

    Summary

    I've experienced an issue where it seems that "deep linking" more than 1 screen (via NavigationLink) "down" doesn't work as expected.

    Steps to reproduce

    1. Replace the body function in the PopToRootExample.swift with the following:
      var body: some View {
        NavigationView {
          RootView(store: Store(
            initialState: RootState(
                timer: .init(),
                first: .init(
                    timer: .init(),
                    second: .init(timer: .init())
                )
            ),
            reducer: Self.rootReducer.debug(),
            environment: ()
          ))
        }
        .navigationViewStyle(StackNavigationViewStyle())
      }
    
    1. Run the app
    2. Tap the "Pop To Root Example"

    Expected behavior

    I'd expect the app to display the Second Screen in the PopToRoot Example.

    Actual behavior

    The app opens up displaying the First Screen, and tapping the "Present Second" button doesn't do anything.

    bug 
    opened by wtsnz 5
  • @Presented property wrapper and optimized presentation

    @Presented property wrapper and optimized presentation

    Thanks so much for your work on this library! I started using the previous iteration of the project in an app with recursive navigation with frequent effects and found that excessive calls to .cancellable and .cancel were causing performance issues. I've updated to this new version and really like the changes you made to how cancellation is handled.

    Summary

    This change refines how cancellation occurs in presented states. Both when effects are wrapped n .cancellable and when .cancel is sent. These effects are fairly costly in TCA [1], and since any action of a presented reducer is wrapped it can really add up.

    Additionally, I introduced a @Presented property wrapper that allows presentation to be further optimized. To minimize change, the library works with property-wrapped values via presented or non-wrapped values via presents. The property-wrapped version is better in all cases though.

    [1] Perhaps additionally so because they incur a lock. I'm looking into that.

    What was done

    • [x] Modify presents to only run the child reducer if the action is in the child domain. This reduces unnecessary wrapping with .cancellable.
    • [x] Add tests for presents to confirm how often .cancel is sent
    • [x] Introduce presented variation of presents that works with the @Presented property wrapper. This version is further optimizes to only .cancel when state goes from non-nil to nil.
    • [x] Signification changes to combine to enable these changes.

    Review notes

    • [ ] It feels like combine should not be public. It's a great utility but now very specific to the internals.
    • [ ] I couldn't find a cleaner way to assert on calls to cancel, thus the DEBUG-only global vars
    • [ ] I'm not yet sure how these changes would adapt to CasePath presentation
    • [ ] The Example should probably be updated to use @Presented
    • [ ] I'm working on a larger sample of recursive navigation based on this, but it's looking good so far - I'll link it when it's finished. I'm also updating my app, which is looking good but I've yet to fully confirm the performance improvements.
    opened by rcarver 4
  • Hide Tababr

    Hide Tababr

    Here is some code we can follow :) working CODE

    public struct HidableTabView<SelectionValue, Content>: View where SelectionValue: Hashable, Content: View {
      let isHidden: Bool
      let selection: Binding<SelectionValue>?
      let content: () -> Content
    
      @State private var currentTabBarHeight: CGFloat = 0
    
      init(
        isHidden: Bool,
        selection: Binding<SelectionValue>?,
        @ViewBuilder content: @escaping () -> Content
      ) {
        self.isHidden = isHidden
        self.selection = selection
        self.content = content
      }
    
      public init(
        isHidden: Bool,
        @ViewBuilder content: @escaping () -> Content
      ) where SelectionValue == Int {
        self.isHidden = isHidden
        self.selection = nil
        self.content = content
      }
    
      public var body: some View {
        tabView
          .stackNavigationViewStyle()
          .padding(.bottom, isHidden ? -self.currentTabBarHeight : 0)
      }
    
      @ViewBuilder var tabView: some View {
        if selection != nil {
          TabView(selection: self.selection!) { self._content }
        } else {
          TabView { self._content }
        }
      }
    
      // swiftlint:disable identifier_name
      @ViewBuilder var _content: some View {
        self.content()
          .background(tabBarHeightReader)
      }
    
      var screenBottomSafeAreaInset: CGFloat {
        (UIApplication.shared.windows.first { $0.isKeyWindow }?.safeAreaInsets.bottom ?? 34)
      }
    
      var tabBarHeightReader: some View {
        GeometryReader { proxy in
          Color.clear
            .onAppear {
              self.currentTabBarHeight = proxy.safeAreaInsets.bottom + screenBottomSafeAreaInset
            }
            .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
              self.currentTabBarHeight = proxy.safeAreaInsets.bottom + screenBottomSafeAreaInset
            }
        }
      }
    }
    
    enhancement 
    opened by saroar 3
  • Import Foundation and update TCA version 0.39.1

    Import Foundation and update TCA version 0.39.1

    Fixed: Pinned combine-schedulers to 0.7.3, which removes an errant @_exported import Foundation. This change may be breaking and require you to add import Foundation in your code.

    so import Foundation fixed issue

    opened by saroar 1
  •  Showing All Messages The package product 'ComposableArchitecture' requires minimum platform version 10.15 for the macOS platform, but this target supports 10.10

    Showing All Messages The package product 'ComposableArchitecture' requires minimum platform version 10.15 for the macOS platform, but this target supports 10.10

    I am using this package inside another package when I start the test I am getting this error I can't run my test here is my app link https://github.com/AddaMeSPB/AddaMeIOS/blob/feature/git_workflow/AddameSPM/Tests/EventViewTests/EventsViewTests.swift#L19

    Steps to reproduce

    you can just grab my app and run test

    Actual behavior

    test should run without any error

    Attachments

    Screenshot 2021-09-15 at 15 47 31

    Showing All Messages The package product 'ComposableArchitecture' requires minimum platform version 10.15 for the macOS platform, but this target supports 10.10

    bug 
    opened by saroar 1
  • Add NavigationDestinationWithStore SwiftUI helper

    Add NavigationDestinationWithStore SwiftUI helper

    Summary

    This PR adds NavigationDestinationWithStore SwiftUI helper, which can be used on iOS >= 16. It wraps the native navigation-destination view modifier and drives it with provided store of optional State and Action.

    What was done

    • [x] Add NavigationDestinationWithStore SwiftUI helper
    • [x] Update DestinationExample
    opened by darrarski 0
  • Add PresentingCaseReducer

    Add PresentingCaseReducer

    Summary

    This PR adds PresentingCaseReducer for presenting given case of provided enum.

    struct First: ReducerProtocol {
      // ...
    }
    
    struct Second: ReducerProtocol {
      // ...
    }
    
    struct Main: ReducerProtocol {
      struct State {
        enum Destination {
          case first(First.State)
          case second(Second.State)
        }
    
        var destination: Destination?
      }
    
      enum Action {
        case first(First.Action)
        case second(Second.Action)
      }
    
      var body: some ReducerProtocol<State, Action> {
        // ...
        .presenting(
          unwrapping: \.destination,
          case: /State.Destination.first,
          id: .notNil(),
          action: /Action.first,
          presented: { First() }
        )
        .presenting(
          unwrapping: \.destination,
          case: /State.Destination.second,
          id: .notNil(),
          action: /Action.second,
          presented: { Second() }
        )
      }
    }
    

    What was done

    • [x] Update package dependencies
    • [x] Add PresentingCaseReducer
    • [x] Add DestinationExample
    • [x] Add documentation
    enhancement 
    opened by darrarski 0
  • Add presentation ID

    Add presentation ID

    Summary

    This PR adds presentationID parameter Reducer and ReducerProtocol methods. The presentationID identifies the presentation and is used to cancel effects on dismission.

    What was done

    • [x] Add presentationID parameter to ReducerProtocol.presenting method.
    • [x] Add presentationID parameter to ReducerProtocol.presentingForEach method.
    • [x] Add presentationID parameter to legacy Reducer.presenting method.
    • [x] Add presentationID parameter to legacy Reducer.presentingForEach method.

    Review notes

    The presentationID parameter defaults to a new UUID and can be customized to AnyHashable value that should uniquely identify the presenting reducer. If this requirement is not met, the effects might not be canceled on dismission, which could lead to unexpected behavior.

    opened by darrarski 0
  • Adopt ReducerProtocol

    Adopt ReducerProtocol

    Summary

    This PR adopts ReducerProtocol.

    More details:

    • https://github.com/pointfreeco/swift-composable-architecture/discussions/1282
    • https://github.com/pointfreeco/swift-composable-architecture/pull/1283

    What was done

    • [x] Switch to protocol-beta branch of Composable Architecture.
    • [x] Add ReducerProtocol version of Reducer+Presenting extension.
    • [x] Add ReducerProtocol version of Reducer+PresentingForEach extension.
    • [x] Soft-deprecate legacy Reducer extensions.
    • [x] Add documentation for ReducerProtocol extensions.
    • [x] Switch to a stable release of Composable Architecture when available.

    Review notes

    ~~⚠️ This is a draft PR with a beta release of the Composable Presentation library. Not suitable for production.~~

    enhancement 
    opened by darrarski 0
  • Fix dependency resolver issues

    Fix dependency resolver issues

    Summary

    For some reason, Swift Package dependency resolving started to fail after the latest release of ComposableArchitecture library (v0.34.0). Not sure what is causing the problem (probably external changes in the dependency graph), but updating version requirement to the latest version solves it.

    What was done

    Review notes

    This should fix the following dependency resolver issue:

    Dependencies could not be resolved because no versions of 'swift-identified-collections' match the requirement 0.3.2..<1.0.0 and root depends on 'swift-composable-architecture' 0.33.1..<1.0.0. 'swift-composable-architecture' >= 0.33.1 practically depends on 'swift-identified-collections' 0.3.2..<1.0.0 because 'swift-composable-architecture' 0.33.1 depends on 'swift-identified-collections' 0.3.2..<1.0.0. 'swift-composable-architecture' >= 0.33.2 practically depends on 'swift-identified-collections' 0.3.2..<1.0.0 because no versions of 'swift-composable-architecture' match the requirement {0.33.2..<0.34.0, 0.34.1..<1.0.0} and 'swift-composable-architecture' 0.34.0 depends on 'swift-identified-collections' 0.3.2..<1.0.0.

    opened by darrarski 0
  • Update dependencies

    Update dependencies

    Summary

    This PR updates package dependencies.

    What was done

    • [x] Update swift-collections to v1.0.2
    • [x] Update swift-case-pats to v0.8.0

    Review notes

    This change should fix #28

    opened by darrarski 0
Releases(v0.8.0)
  • v0.8.0(Dec 20, 2022)

    What's Changed

    • Add NavigationDestinationWithStore SwiftUI helper by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/35

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.7.0...v0.8.0

    Source code(tar.gz)
    Source code(zip)
  • v0.7.0(Dec 20, 2022)

    What's Changed

    • Add PresentingCaseReducer by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/34

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.6.1...v0.7.0

    Source code(tar.gz)
    Source code(zip)
  • v0.6.1(Nov 10, 2022)

    What's Changed

    • Add presentation ID by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/33

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.6.0...v0.6.1

    Source code(tar.gz)
    Source code(zip)
  • v0.6.0(Oct 16, 2022)

    What's Changed

    • Adopt ReducerProtocol by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/32

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.5.3...v0.6.0

    Source code(tar.gz)
    Source code(zip)
  • v0.5.3(Aug 29, 2022)

    What's Changed

    • Import Foundation and update TCA version 0.39.1 by @saroar in https://github.com/darrarski/swift-composable-presentation/pull/31

    New Contributors

    • @saroar made their first contribution in https://github.com/darrarski/swift-composable-presentation/pull/31

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.5.2...v0.5.3

    Source code(tar.gz)
    Source code(zip)
  • v0.5.2(Mar 18, 2022)

    What's Changed

    • Fix dependency resolver issues by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/30

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.5.1...v0.5.2

    Source code(tar.gz)
    Source code(zip)
  • v0.5.1(Mar 15, 2022)

    What's Changed

    • Update dependencies by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/29

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.5.0...v0.5.1

    Source code(tar.gz)
    Source code(zip)
  • v0.5.0(Feb 23, 2022)

    What's Changed

    • Improved effect cancelation when replacing the presented state with another one (without dismissing it first).
    • Improved support for multi-column NavigationView on iPadOS.
    • Added example that shows NavigationLink usage on a list.

    Pull Requests

    • Local (presented) state identification by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/27

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.4.1...v0.5.0

    Source code(tar.gz)
    Source code(zip)
  • v0.4.1(Jan 14, 2022)

    What's Changed

    • Update ComposableArchitecture by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/26

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.4.0...v0.4.1

    Source code(tar.gz)
    Source code(zip)
  • v0.4.0(Dec 6, 2021)

    What's Changed

    • Add presentation actions to Reducer.presenting by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/24
    • Unify Reducer.presenting API by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/25

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.3.7...v0.4.0

    Source code(tar.gz)
    Source code(zip)
  • v0.3.7(Nov 29, 2021)

    What's Changed

    • Update sheet example - Add a button for programmatically dismissing the sheet by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/21

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.3.6...v0.3.7

    Source code(tar.gz)
    Source code(zip)
  • v0.3.6(Nov 17, 2021)

  • v0.3.5(Nov 17, 2021)

    What's Changed

    • Refactor Reducer.presenting by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/19

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.3.4...v0.3.5

    Source code(tar.gz)
    Source code(zip)
  • v0.3.4(Nov 15, 2021)

    What's Changed

    • Add SwitchStore usage example by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/18

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.3.3...v0.3.4

    Source code(tar.gz)
    Source code(zip)
  • v0.3.3(Nov 14, 2021)

    What's Changed

    • Improve Reducer+PresentingForEach debuging by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/15
    • Update iOS example app by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/16

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.3.2...v0.3.3

    Source code(tar.gz)
    Source code(zip)
  • v0.3.2(Nov 12, 2021)

    What's Changed

    • API improvements by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/14

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.3.1...v0.3.2

    Source code(tar.gz)
    Source code(zip)
  • v0.3.1(Nov 12, 2021)

    What's Changed

    • Presenting for each by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/13

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.3.0...v0.3.1

    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(Nov 12, 2021)

    PRs

    • Improve effect cancellation on dismiss by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/5
    • Improve API by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/6
    • Add initial macOS support by @darrarski in https://github.com/darrarski/swift-composable-presentation/pull/11

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.2.1...v0.3.0

    Kudos to @rcarver for suggesting performance improvements in #4

    ⚠️ This release introduces breaking API changes (Reducer.presents renamed to Reducer.presenting, some parameter names changed, etc.) that should be easy to adapt quickly and do not change the actual functionality.

    In addition, there are new helpers for SwiftUI presentation, like popover and fullScreenCover modifiers that work with Store.

    Source code(tar.gz)
    Source code(zip)
  • v0.2.1(Nov 8, 2021)

    • Add breakpointOnNil parameter to presents modifier

    Full Changelog: https://github.com/darrarski/swift-composable-presentation/compare/v0.2.0...v0.2.1

    Source code(tar.gz)
    Source code(zip)
  • v0.2.0(Sep 8, 2021)

    • Added a new variant of Reducer.presents modifier that works on CasePaths.
    • Added general-purpose Reducer.combine(with:cancelEffects:) modifier that can be used for more complex states, whenever effect cancellation is important.
    • Refactored code and updated example app.
    Source code(tar.gz)
    Source code(zip)
  • v0.1.1(Sep 5, 2021)

  • v0.1.0(Sep 2, 2021)

    • Combine reducers using the .presents modifier.
    • Use NavigationLink with a Store that contains an optional state.
    • Use View.sheet with a Store that contains an optional state.
    Source code(tar.gz)
    Source code(zip)
Owner
Dariusz Rybicki
Software Engineer, iOS & Mac Developer, Scrum Master
Dariusz Rybicki
A drop-in universal library helps you to manage the navigation bar styles and makes transition animations smooth between different navigation bar styles

A drop-in universal library helps you to manage the navigation bar styles and makes transition animations smooth between different navigation bar styles while pushing or popping a view controller for all orientations. And you don't need to write any line of code for it, it all happens automatically.

Zhouqi Mo 3.3k Dec 21, 2022
sRouting - The lightweight navigation framework for SwiftUI.

sRouting The lightweight navigation framework for SwiftUI. Overview sRouting using the native navigation mechanism in SwiftUI. It's easy to handle nav

Shiro 8 Aug 15, 2022
FlowStacks allows you to hoist SwiftUI navigation and presentation state into a Coordinator

FlowStacks allow you to manage complex SwiftUI navigation and presentation flows with a single piece of state. This makes it easy to hoist that state into a high-level coordinator view. Using this pattern, you can write isolated views that have zero knowledge of their context within the navigation flow of an app.

John Patrick Morgan 471 Jan 3, 2023
Change SwiftUI Navigation Bar Color for different View

SwiftUINavigationBarColor Change SwiftUI NavigationBar background color per screen. Usage For NavigationBarColor to work, you have to set the Navigati

Hai Feng Kao 18 Jul 15, 2022
🧭 SwiftUI navigation done right

?? NavigationKit NavigationKit is a lightweight library which makes SwiftUI navigation super easy to use. ?? Installation ?? Swift Package Manager Usi

Alex Nagy 152 Dec 27, 2022
Make SwiftUI Navigation be easy

VNavigator VNavigator is a clean and easy-to-use navigation in SwiftUI base on UINavigationController in UIKit Installation From CocoaPods CocoaPods i

Vu Vuong 10 Dec 6, 2022
Tools for making SwiftUI navigation simpler, more ergonomic and more precise.

SwiftUI Navigation Tools for making SwiftUI navigation simpler, more ergonomic and more precise. Motivation Tools Navigation overloads Navigation view

Point-Free 1.1k Jan 1, 2023
SwiftUINavigation provides UIKit-like navigation in SwiftUI

SwiftUINavigation About SwiftUINavigation provides UIKit-like navigation in Swif

Bhimsen Padalkar 1 Mar 28, 2022
A lightweight iOS mini framework that enables programmatic navigation with SwiftUI, by using UIKit under the hood.

RouteLinkKit A lightweight iOS mini framework that enables programmatic navigation with SwiftUI. RouteLinkKit is fully compatible with native Navigati

Αθανάσιος Κεφαλάς 4 Feb 8, 2022
SwiftUINavigator: a lightweight, flexible, and super easy library which makes SwiftUI navigation a trivial task

The logo is contributed with ❤️ by Mahmoud Hussein SwiftUINavigator is a lightwe

OpenBytes 22 Dec 21, 2022
Simple iOS app to showcase navigation with coordinators in SwiftUI + MVVM.

SimpleNavigation Simple iOS app to showcase the use of the Coordinator pattern using SwiftUI and MVVM. The implementation is as easy as calling a push

Erik Lopez 7 Dec 6, 2022
Custom navigation swiftui NavigationLink NavigationView

Custom navigation swiftui Experimenting with navigation link. if you find this idea interesting you can take and expend it into a more powerful soluti

Igor 5 Dec 2, 2022
Backported SwiftUI navigation APIs introduced in WWDC22

Navigation Backport This package uses the navigation APIs available in older SwiftUI versions (such as NavigationView and NavigationLink) to recreate

John Patrick Morgan 532 Dec 29, 2022
An open source library for building deep-linkable SwiftUI applications with composition, testing and ergonomics in mind

Composable Navigator An open source library for building deep-linkable SwiftUI applications with composition, testing and ergonomics in mind Vanilla S

Bahn-X 539 Jan 8, 2023
Models UI navigation patterns using TCA

Composable Navigation The Composable Navigation is a Swift Package that builds on top of The Composable Architecture (TCA, for short). It models UI na

Michael Heinzl 41 Dec 14, 2022
An iOS view-controller navigation management. No inherit, using one line code to integrate.

KGNavigationBar Example An iOS view-controller navigation management. No inherit, using one line code to integrate. 一个 iOS 控制器导航管理库. 无需继承, 一行代码即可实现集成。

VanJay 5 Sep 6, 2021
Powerful navigation in the Composable Architecture via the coordinator pattern

TCACoordinators The coordinator pattern in the Composable Architecture TCACoordinators brings a flexible approach to navigation in SwiftUI using the C

John Patrick Morgan 231 Jan 7, 2023
A wrapper for NavigationView and NavigationLink that makes programmatic navigation a little friendlier.

NavigatorKit A wrapper for NavigationView and NavigationLink that makes programmatic navigation a little friendlier. NavigatorKit is an opinionated wr

Gyuri Grell 2 Jun 16, 2022
Cordova/Phonegap plugin for launching today's most popular navigation/ride apps to navigate to a destination.

Launch Navigator Cordova/Phonegap Plugin Cordova/Phonegap plugin for launching today's most popular navigation/ride apps to navigate to a destination.

null 0 Oct 25, 2021