From 5e031cd0b829cd43b409807597bd5b7e1c01e319 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Wed, 19 Feb 2025 08:36:39 -0800 Subject: [PATCH 1/3] Add `traitCollection.push(state:)` --- .../UIKit/NavigationStackControllerUIKit.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Sources/ComposableArchitecture/UIKit/NavigationStackControllerUIKit.swift b/Sources/ComposableArchitecture/UIKit/NavigationStackControllerUIKit.swift index da5c584cead1..8bf41a80670c 100644 --- a/Sources/ComposableArchitecture/UIKit/NavigationStackControllerUIKit.swift +++ b/Sources/ComposableArchitecture/UIKit/NavigationStackControllerUIKit.swift @@ -2,7 +2,6 @@ import UIKit extension NavigationStackController { - /// Drives a navigation stack controller with a store. /// /// See the dedicated article on for more information on the library's @@ -69,4 +68,13 @@ } } } + + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) + @MainActor + extension UIPushAction: Sendable { + public func callAsFunction(state: Element) { + @Dependency(\.stackElementID) var stackElementID + self(value: StackState.Component(id: stackElementID(), element: state)) + } + } #endif From c7f36fd63abb96c288ae59444c0232c6ddf25c9d Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Wed, 19 Feb 2025 08:44:25 -0800 Subject: [PATCH 2/3] wip --- .../Documentation.docc/Extensions/UIKit.md | 7 ++++++- .../UIKit/NavigationStackControllerUIKit.swift | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Sources/ComposableArchitecture/Documentation.docc/Extensions/UIKit.md b/Sources/ComposableArchitecture/Documentation.docc/Extensions/UIKit.md index e73bfa637504..90f76f54da9a 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Extensions/UIKit.md +++ b/Sources/ComposableArchitecture/Documentation.docc/Extensions/UIKit.md @@ -16,7 +16,12 @@ integrate into application code written in UIKit. ### Presenting alerts and action sheets -- ``UIKit/UIAlertController/init(store:)`` +- ``UIKit/UIAlertController`` + +### Stack-based navigation + +- ``UIKitNavigation/NavigationStackController`` +- ``UIKitNavigation/UIPushAction`` ### Combine integration diff --git a/Sources/ComposableArchitecture/UIKit/NavigationStackControllerUIKit.swift b/Sources/ComposableArchitecture/UIKit/NavigationStackControllerUIKit.swift index 8bf41a80670c..bfe1a0493e3d 100644 --- a/Sources/ComposableArchitecture/UIKit/NavigationStackControllerUIKit.swift +++ b/Sources/ComposableArchitecture/UIKit/NavigationStackControllerUIKit.swift @@ -72,6 +72,12 @@ @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @MainActor extension UIPushAction: Sendable { + /// Pushes an element of ``StackState`` onto the current navigation stack. + /// + /// This is the UIKit equivalent of + /// ``SwiftUI/NavigationLink/init(state:label:fileID:filePath:line:column:)``. + /// + /// - Parameter state: An element of stack state. public func callAsFunction(state: Element) { @Dependency(\.stackElementID) var stackElementID self(value: StackState.Component(id: stackElementID(), element: state)) From d0759955fca02e32a50bd7aec6161fd55c2e98d0 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Tue, 25 Feb 2025 12:04:55 -0800 Subject: [PATCH 3/3] Fix --- .../xcshareddata/swiftpm/Package.resolved | 6 ++-- Package.swift | 2 +- Package@swift-6.0.swift | 2 +- .../ComposableArchitecture/TestStore.swift | 34 ++++++++----------- .../NavigationStackControllerUIKit.swift | 25 +++++++++++--- 5 files changed, 40 insertions(+), 29 deletions(-) diff --git a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved index 94998dfcc7d3..89ba4b1bca4b 100644 --- a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "7bde44da63d64357331b7d73c1c3f9d1aa732b1793a1d1fc5879f2c192bef69c", + "originHash" : "3ebb70e4ef5203d6f8bebecc1bec69837803b880f6f397952a6f49aa1f6fe107", "pins" : [ { "identity" : "combine-schedulers", @@ -105,8 +105,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-navigation", "state" : { - "revision" : "e28911721538fa0c2439e92320bad13e3200866f", - "version" : "2.2.3" + "revision" : "db6bc9dbfed001f21e6728fd36413d9342c235b4", + "version" : "2.3.0" } }, { diff --git a/Package.swift b/Package.swift index 387d16f78460..043e869e5b00 100644 --- a/Package.swift +++ b/Package.swift @@ -26,7 +26,7 @@ let package = Package( .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.4.0"), .package(url: "https://github.com/pointfreeco/swift-identified-collections", from: "1.1.0"), .package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.2.0"), - .package(url: "https://github.com/pointfreeco/swift-navigation", from: "2.2.2"), + .package(url: "https://github.com/pointfreeco/swift-navigation", from: "2.3.0"), .package(url: "https://github.com/pointfreeco/swift-perception", from: "1.3.4"), .package(url: "https://github.com/pointfreeco/swift-sharing", "0.1.2"..<"3.0.0"), .package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.3.0"), diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift index 15705d2b5210..ef27ccbf0b81 100644 --- a/Package@swift-6.0.swift +++ b/Package@swift-6.0.swift @@ -26,7 +26,7 @@ let package = Package( .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.4.0"), .package(url: "https://github.com/pointfreeco/swift-identified-collections", from: "1.1.0"), .package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.2.0"), - .package(url: "https://github.com/pointfreeco/swift-navigation", from: "2.2.2"), + .package(url: "https://github.com/pointfreeco/swift-navigation", from: "2.3.0"), .package(url: "https://github.com/pointfreeco/swift-perception", from: "1.3.4"), .package(url: "https://github.com/pointfreeco/swift-sharing", "0.1.2"..<"3.0.0"), .package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.3.0"), diff --git a/Sources/ComposableArchitecture/TestStore.swift b/Sources/ComposableArchitecture/TestStore.swift index c2a0a2cd6a1f..10f74dd49e65 100644 --- a/Sources/ComposableArchitecture/TestStore.swift +++ b/Sources/ComposableArchitecture/TestStore.swift @@ -729,30 +729,24 @@ public final class TestStore { line: UInt, column: UInt ) { - // NB: This existential opening can go away if we can constrain 'State: Equatable' at the - // 'TestStore' level, but for some reason this breaks DocC. - if self.sharedChangeTracker.hasChanges, let stateType = State.self as? any Equatable.Type { - func open(_: EquatableState.Type) { - let store = self as! TestStore - try? store.expectedStateShouldMatch( - preamble: "Test store finished before asserting against changes to shared state", - postamble: """ + if sharedChangeTracker.hasChanges { + try? expectedStateShouldMatch( + preamble: "Test store finished before asserting against changes to shared state", + postamble: """ Invoke "TestStore.assert" at the end of this test to assert against changes to shared \ state. """, - expected: store.state, - actual: store.state, - updateStateToExpectedResult: nil, - skipUnnecessaryModifyFailure: true, - fileID: fileID, - filePath: filePath, - line: line, - column: column - ) - } - open(stateType) - self.sharedChangeTracker.reset() + expected: state, + actual: state, + updateStateToExpectedResult: nil, + skipUnnecessaryModifyFailure: true, + fileID: fileID, + filePath: filePath, + line: line, + column: column + ) } + sharedChangeTracker.reset() } /// Overrides the store's dependencies for a given operation. diff --git a/Sources/ComposableArchitecture/UIKit/NavigationStackControllerUIKit.swift b/Sources/ComposableArchitecture/UIKit/NavigationStackControllerUIKit.swift index bfe1a0493e3d..bf4c29703ce1 100644 --- a/Sources/ComposableArchitecture/UIKit/NavigationStackControllerUIKit.swift +++ b/Sources/ComposableArchitecture/UIKit/NavigationStackControllerUIKit.swift @@ -71,16 +71,33 @@ @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @MainActor - extension UIPushAction: Sendable { + extension UIPushAction { /// Pushes an element of ``StackState`` onto the current navigation stack. /// /// This is the UIKit equivalent of /// ``SwiftUI/NavigationLink/init(state:label:fileID:filePath:line:column:)``. /// - /// - Parameter state: An element of stack state. - public func callAsFunction(state: Element) { + /// - Parameters: + /// - state: An element of stack state. + /// - fileID: The source `#fileID` associated with the push. + /// - filePath: The source `#filePath` associated with the push. + /// - line: The source `#line` associated with the push. + /// - column: The source `#column` associated with the push. + public func callAsFunction( + state: Element, + fileID: StaticString = #fileID, + filePath: StaticString = #filePath, + line: UInt = #line, + column: UInt = #column + ) { @Dependency(\.stackElementID) var stackElementID - self(value: StackState.Component(id: stackElementID(), element: state)) + self( + value: StackState.Component(id: stackElementID(), element: state), + fileID: fileID, + filePath: filePath, + line: line, + column: column + ) } } #endif