Skip to content

Commit

Permalink
feat: API Improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
maximkrouk committed Dec 22, 2023
1 parent 844dd5e commit 45dda86
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 123 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ final class MyViewController: UIViewController {
func bindViewModel() {
navigationDestination(
viewModel.publisher(for: \.state.route),
switch: destinations { destinations, route in
switch: { destinations, route in
switch route {
case .details:
destinations.$detailsController
Expand Down Expand Up @@ -110,7 +110,7 @@ final class MyViewController: UIViewController {
func bindViewModel() {
navigationStack(
viewModel.publisher(for: \.state.path),
switch: destinations { destinations, route in
switch: { destinations, route in
switch route {
case .featureA:
destinations.$featureAControllers
Expand Down
124 changes: 34 additions & 90 deletions Sources/CombineNavigation/CocoaViewController+API.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import FoundationExtensions

// MARK: navigationStack

extension CocoaViewController {
extension RoutingController {
/// Subscribes on publisher of navigation stack state
@inlinable
public func navigationStack<
P: Publisher,
C: Collection & Equatable,
Route: Hashable
>(
_ publisher: P,
switch destination: @escaping (Route) -> any GrouppedDestinationProtocol<C.Index>,
switch destination: @escaping (Destinations, Route) -> any GrouppedDestinationProtocol<C.Index>,
onPop: @escaping ([C.Index]) -> Void
) -> Cancellable where
P.Output == C,
Expand All @@ -27,61 +28,13 @@ extension CocoaViewController {
{
combineNavigationRouter.navigationStack(
publisher,
switch: destination,
onPop: onPop
)
}

/// Subscribes on publisher of navigation stack state
public func navigationStack<
P: Publisher,
C: Collection & Equatable,
Route: Hashable
>(
_ publisher: P,
switch controller: @escaping (Route, C.Index) -> CocoaViewController,
onPop: @escaping ([C.Index]) -> Void
) -> Cancellable where
P.Output == C,
P.Failure == Never,
C.Element == Route,
C.Index: Hashable,
C.Indices: Equatable
{
combineNavigationRouter.navigationStack(
publisher,
switch: controller,
onPop: onPop
)
}

/// Subscribes on publisher of navigation stack state
public func navigationStack<
P: Publisher,
Stack,
IDs: Collection & Equatable,
Route
>(
_ publisher: P,
ids: @escaping (Stack) -> IDs,
route: @escaping (Stack, IDs.Element) -> Route?,
switch destination: @escaping (Route) -> any GrouppedDestinationProtocol<IDs.Element>,
onPop: @escaping ([IDs.Element]) -> Void
) -> Cancellable where
P.Output == Stack,
P.Failure == Never,
IDs.Element: Hashable
{
combineNavigationRouter.navigationStack(
publisher,
ids: ids,
route: route,
switch: destination,
switch: destinations(destination),
onPop: onPop
)
}

/// Subscribes on publisher of navigation stack state
@inlinable
public func navigationStack<
P: Publisher,
Stack,
Expand All @@ -91,7 +44,7 @@ extension CocoaViewController {
_ publisher: P,
ids: @escaping (Stack) -> IDs,
route: @escaping (Stack, IDs.Element) -> Route?,
switch controller: @escaping (Route, IDs.Element) -> CocoaViewController,
switch destination: @escaping (Destinations, Route) -> any GrouppedDestinationProtocol<IDs.Element>,
onPop: @escaping ([IDs.Element]) -> Void
) -> Cancellable where
P.Output == Stack,
Expand All @@ -102,16 +55,17 @@ extension CocoaViewController {
publisher,
ids: ids,
route: route,
switch: controller,
switch: destinations(destination),
onPop: onPop
)
}
}

// MARK: navigationDestination

extension CocoaViewController {
extension RoutingController {
/// Subscribes on publisher of navigation destination state
@inlinable
public func navigationDestination<P: Publisher>(
_ id: AnyHashable,
isPresented publisher: P,
Expand All @@ -130,27 +84,10 @@ extension CocoaViewController {
}

/// Subscribes on publisher of navigation destination state
public func navigationDestination<P: Publisher>(
_ id: AnyHashable,
isPresented publisher: P,
controller: @escaping () -> CocoaViewController,
onPop: @escaping () -> Void
) -> AnyCancellable where
P.Output == Bool,
P.Failure == Never
{
combineNavigationRouter.navigationDestination(
id,
isPresented: publisher,
controller: controller,
onPop: onPop
)
}

/// Subscribes on publisher of navigation destination state
@inlinable
public func navigationDestination<P: Publisher, Route>(
_ publisher: P,
switch destination: @escaping (Route) -> SingleDestinationProtocol,
switch destination: @escaping (Destinations, Route) -> SingleDestinationProtocol,
onPop: @escaping () -> Void
) -> AnyCancellable where
Route: Hashable,
Expand All @@ -159,26 +96,33 @@ extension CocoaViewController {
{
combineNavigationRouter.navigationDestination(
publisher,
switch: destination,
switch: destinations(destination),
onPop: onPop
)
}
}

/// Subscribes on publisher of navigation destination state
public func navigationDestination<P: Publisher, Route>(
_ publisher: P,
switch controller: @escaping (Route) -> CocoaViewController,
onPop: @escaping () -> Void
) -> AnyCancellable where
Route: Hashable,
P.Output == Route?,
P.Failure == Never
{
combineNavigationRouter.navigationDestination(
publisher,
switch: controller,
onPop: onPop
)
// MARK: - Internal helpers

extension RoutingController {
@usableFromInline
internal func destinations<Route>(
_ mapping: @escaping (Destinations, Route) -> SingleDestinationProtocol
) -> (Route) -> SingleDestinationProtocol {
let destinations = _makeDestinations()
return { route in
mapping(destinations, route)
}
}

@usableFromInline
internal func destinations<Route, DestinationID: Hashable>(
_ mapping: @escaping (Destinations, Route) -> any GrouppedDestinationProtocol<DestinationID>
) -> (Route) -> any GrouppedDestinationProtocol<DestinationID> {
let destinations = _makeDestinations()
return { route in
mapping(destinations, route)
}
}
}
#endif
11 changes: 9 additions & 2 deletions Sources/CombineNavigation/Internal/CombineNavigationRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,28 @@ import Combine
import FoundationExtensions

extension CocoaViewController {
var combineNavigationRouter: CombineNavigationRouter {
@usableFromInline
internal var combineNavigationRouter: CombineNavigationRouter {
getAssociatedObject(forKey: #function) ?? {
let router = CombineNavigationRouter(self)
setAssociatedObject(router, forKey: #function)
return router
}()
}

@inlinable
public func addRoutedChild(_ controller: CocoaViewController) {
combineNavigationRouter.addChild(controller.combineNavigationRouter)
addChild(controller)
}
}

extension CombineNavigationRouter {
@usableFromInline
class NavigationRoute: Identifiable {
@usableFromInline
let id: AnyHashable

let routingControllerID: ObjectIdentifier
private(set) var routedControllerID: ObjectIdentifier?
private let controller: () -> CocoaViewController?
Expand Down Expand Up @@ -50,6 +55,7 @@ extension CombineNavigationRouter {
}
}

@usableFromInline
final class CombineNavigationRouter: Weakifiable {
fileprivate weak var parent: CombineNavigationRouter?
fileprivate weak var node: CocoaViewController!
Expand All @@ -67,7 +73,8 @@ final class CombineNavigationRouter: Weakifiable {
self.node = node
}

fileprivate func addChild(_ router: CombineNavigationRouter) {
@usableFromInline
internal func addChild(_ router: CombineNavigationRouter) {
router.parent = self
directChildren.removeAll(where: { $0.object === router })
directChildren.append(.init(router))
Expand Down
22 changes: 0 additions & 22 deletions Sources/CombineNavigation/RoutingController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,4 @@ public protocol RoutingController: CocoaViewController {
associatedtype Destinations
func _makeDestinations() -> Destinations
}

extension RoutingController {
@inlinable
public func destinations<Route>(
_ mapping: @escaping (Destinations, Route) -> SingleDestinationProtocol
) -> (Route) -> SingleDestinationProtocol {
let destinations = _makeDestinations()
return { route in
mapping(destinations, route)
}
}

@inlinable
public func destinations<Route, DestinationID: Hashable>(
_ mapping: @escaping (Destinations, Route) -> any GrouppedDestinationProtocol<DestinationID>
) -> (Route) -> any GrouppedDestinationProtocol<DestinationID> {
let destinations = _makeDestinations()
return { route in
mapping(destinations, route)
}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ fileprivate class TreeViewController: CocoaViewController {
func bind<P: Publisher<TreeViewModel.State, Never>>(_ publisher: P) {
navigationDestination(
publisher.map(\.destination?.tag).removeDuplicates(),
switch: destinations { destinations, route in
switch: { destinations, route in
switch route {
case .orderDetail:
destinations.$orderDetailController
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ fileprivate class StackViewController: CocoaViewController {
func bind<P: Publisher<StackViewModel.State, Never>>(_ publisher: P) {
navigationStack(
publisher.map(\.path).map { $0.map(\.tag) }.removeDuplicates(),
switch: destinations { destinations, route in
switch: { destinations, route in
switch route {
case .orderDetail:
destinations.$orderDetailControllers
Expand Down
9 changes: 5 additions & 4 deletions Tests/CombineNavigationTests/RoutingControllerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ fileprivate class TreeViewController: CocoaViewController {

navigationDestination(
publisher.map(\.destination?.tag).removeDuplicates(),
switch: destinations { destinations, route in
switch: { destinations, route in
switch route {
case .tree:
destinations.$treeController
Expand Down Expand Up @@ -306,12 +306,12 @@ fileprivate class StackViewController: CocoaViewController {

navigationStack(
publisher.map(\.path).map { $0.map(\.tag) }.removeDuplicates(),
switch: destinations { destination, route in
switch: { destinations, route in
switch route {
case .tree:
destination.$treeControllers
destinations.$treeControllers
case .stack:
destination.$stackControllers
destinations.$stackControllers
}
},
onPop: capture { _self, indices in
Expand All @@ -320,5 +320,6 @@ fileprivate class StackViewController: CocoaViewController {
)
.store(in: &cancellables)
}

}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ fileprivate class TreeViewController: CocoaViewController {
func bind<P: Publisher<TreeViewModel.State, Never>>(_ publisher: P) {
navigationDestination(
publisher.map(\.destination?.tag).removeDuplicates(),
switch: destinations { destinations, route in
switch: { destinations, route in
switch route {
case .orderDetail:
destinations.$orderDetailController
Expand Down

0 comments on commit 45dda86

Please sign in to comment.