Skip to content

Commit a79b310

Browse files
authored
chore: Swift 6 (#19)
* chore: Swift 6 * chore: update ci environments * chore: conditionalize package the other way * fix: Package.swift * chore: audit Sendable * chore: use Swift 6 ci branch * chore: string concurrency in Swift 6 * chore: swaps @swift files around * chore: only use @retroactive on swift 6+ * chore: add missing tests * chore: move back onto ci@main --------- Co-authored-by: danthorpe <[email protected]>
1 parent 09b9343 commit a79b310

File tree

12 files changed

+186
-17
lines changed

12 files changed

+186
-17
lines changed

.github/workflows/main.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ jobs:
2929
{
3030
"os": "macos-14",
3131
"xcode": "15.4"
32+
},
33+
{
34+
"os": "macos-14",
35+
"xcode": "16.0"
3236
}
3337
]
3438
}

[email protected]

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// swift-tools-version: 6.0
2+
@preconcurrency import PackageDescription
3+
4+
var package = Package(
5+
name: "swift-composable-loadable",
6+
platforms: [
7+
.macOS(.v13),
8+
.iOS(.v16),
9+
.tvOS(.v16),
10+
.watchOS(.v9),
11+
],
12+
products: [
13+
.library(name: "ComposableLoadable", targets: ["ComposableLoadable"])
14+
],
15+
dependencies: [
16+
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.3.0"),
17+
.package(url: "https://github.com/pointfreeco/swift-composable-architecture", from: "1.15.0"),
18+
],
19+
targets: [
20+
.target(
21+
name: "ComposableLoadable",
22+
dependencies: [
23+
.product(
24+
name: "ComposableArchitecture", package: "swift-composable-architecture"
25+
)
26+
]
27+
),
28+
.target(
29+
name: "CommonTestHelpers",
30+
dependencies: [
31+
"ComposableLoadable",
32+
.product(
33+
name: "ComposableArchitecture", package: "swift-composable-architecture"
34+
),
35+
]
36+
),
37+
.testTarget(
38+
name: "ComposableLoadableTests",
39+
dependencies: [
40+
"CommonTestHelpers",
41+
"ComposableLoadable",
42+
]
43+
),
44+
]
45+
)
46+
47+
extension Target.Dependency {
48+
static let composableArchitecture: Target.Dependency = .product(
49+
name: "ComposableArchitecture", package: "swift-composable-architecture"
50+
)
51+
}
52+
53+
extension [SwiftSetting] {
54+
static let concurrency: Self = [
55+
.enableUpcomingFeature("StrictConcurrency"),
56+
.enableUpcomingFeature("InferSendableFromCaptures"),
57+
]
58+
}

Sources/CommonTestHelpers/ParentFeature.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,47 @@ struct ParentFeature {
2323
}
2424
}
2525
}
26+
27+
@Reducer
28+
struct RandomFeature {
29+
@ObservableState
30+
struct State: Equatable {
31+
@ObservationStateIgnored
32+
@LoadableState<EmptyLoadRequest, CounterFeature.State> package var counter
33+
}
34+
enum Action: Equatable {
35+
case counter(LoadingActionWith<EmptyLoadRequest, CounterFeature>)
36+
}
37+
@Dependency(\.testClient.getRandomValue) var getRandomValue
38+
var body: some ReducerOf<Self> {
39+
Reduce { _, _ in
40+
return .none
41+
}
42+
.loadable(\.$counter, action: \.counter) {
43+
CounterFeature()
44+
} load: { _ in
45+
CounterFeature.State(count: try await getRandomValue())
46+
}
47+
}
48+
}
49+
50+
@Reducer
51+
struct ChildlessFeature {
52+
@ObservableState
53+
struct State: Equatable {
54+
@ObservationStateIgnored
55+
@LoadableState<String, Int> package var counterValue
56+
}
57+
enum Action: Equatable {
58+
case counterValue(LoadingAction<String, Int, NoLoadingAction>)
59+
}
60+
@Dependency(\.testClient.getValue) var getValue
61+
var body: some ReducerOf<Self> {
62+
Reduce { _, _ in
63+
return .none
64+
}
65+
.loadable(\.$counterValue, action: \.counterValue) { request, _ in
66+
try await getValue(request)
67+
}
68+
}
69+
}

Sources/CommonTestHelpers/TestFeatureClient.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
import ComposableArchitecture
1+
import Dependencies
2+
import DependenciesMacros
23

3-
struct TestFeatureClient: TestDependencyKey {
4-
static let testValue = TestFeatureClient(
5-
getValue: unimplemented("TestFeatureClient.getValue")
6-
)
4+
@DependencyClient
5+
struct TestFeatureClient {
76
var getValue: @Sendable (String) async throws -> Int
7+
var getRandomValue: @Sendable () async throws -> Int
8+
}
9+
10+
extension TestFeatureClient: TestDependencyKey {
11+
static let testValue = TestFeatureClient()
812
}
913

1014
extension DependencyValues {

Sources/ComposableLoadable/Loadable/LoadableClient.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Foundation
33

44
// MARK: - Loadable Client
55

6-
package protocol LoadableClient<Request, State, Value> {
6+
package protocol LoadableClient<Request, State, Value>: Sendable {
77
associatedtype Value
88
associatedtype State
99
associatedtype Request

Sources/ComposableLoadable/Loadable/LoadableState.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,3 +292,11 @@ extension LoadableState: Equatable where Value: Equatable {
292292
lhs.current == rhs.current && lhs.previous == rhs.previous
293293
}
294294
}
295+
296+
extension LoadedValue: Sendable where Request: Sendable, Value: Sendable {}
297+
298+
extension LoadedFailure: Sendable where Request: Sendable, Failure: Sendable {}
299+
300+
extension LoadableState.State: Sendable where Request: Sendable, Value: Sendable {}
301+
302+
extension LoadableState: Sendable where Request: Sendable, Value: Sendable {}

Sources/ComposableLoadable/Loadable/LoadingReducer.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Foundation
33

44
// MARK: - Public API
55

6-
extension Reducer {
6+
extension Reducer where State: Sendable {
77

88
/// Integrate a Loadable child domain with a generic Request type
99
public func loadable<
@@ -18,7 +18,7 @@ extension Reducer {
1818
load: @escaping @Sendable (Request, State) async throws -> ChildState,
1919
fileID: StaticString = #fileID,
2020
line: UInt = #line
21-
) -> some ReducerOf<Self> where ChildState == Child.State, ChildAction == Child.Action {
21+
) -> some ReducerOf<Self> where Child.State: Sendable, ChildState == Child.State, ChildAction == Child.Action {
2222
loadable(
2323
fileID: fileID,
2424
line: line,
@@ -43,7 +43,7 @@ extension Reducer {
4343
load: @escaping @Sendable (State) async throws -> ChildState,
4444
fileID: StaticString = #fileID,
4545
line: UInt = #line
46-
) -> some ReducerOf<Self> where ChildState == Child.State, ChildAction == Child.Action {
46+
) -> some ReducerOf<Self> where Child.State: Sendable, ChildState == Child.State, ChildAction == Child.Action {
4747
loadable(
4848
fileID: fileID,
4949
line: line,
@@ -79,7 +79,7 @@ extension Reducer {
7979

8080
// MARK: - Internal API
8181

82-
extension Reducer {
82+
extension Reducer where State: Sendable {
8383

8484
fileprivate func loadable<
8585
Child: Reducer,

Sources/ComposableLoadable/Pagination/PaginationFeature+.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// MARK: - Equatable Conformance
2+
13
extension PaginationFeature.State.Page: Equatable where Element: Equatable {
24
public static func == (lhs: Self, rhs: Self) -> Bool {
35
lhs.previous == rhs.previous

Sources/ComposableLoadable/Pagination/PaginationFeature.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import Foundation
88
/// has already be fetched. i.e. you fetch some elements, and have
99
/// pagination cursors for previous & next. You can use
1010
/// `LoadableState` etc to do this.
11-
@Reducer public struct PaginationFeature<Element: Identifiable & Sendable>: Sendable {
11+
@Reducer public struct PaginationFeature<Element: Identifiable & Sendable>: Sendable where Element.ID: Sendable {
1212

1313
@ObservableState
14-
public struct State {
14+
public struct State: Sendable {
1515
/// Access the `PaginationContext`
1616
public internal(set) var context: any PaginationContext
1717

Sources/ComposableLoadable/Views/PaginationLoadMore.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public struct PaginationLoadMore<
77
Failure: View,
88
Loading: View,
99
NoMoreResults: View
10-
>: View {
10+
>: View where Element.ID: Sendable {
1111

1212
public typealias Retry = () -> Void
1313
public typealias FailureViewBuilder = (any Error, @escaping Retry) -> Failure
@@ -55,7 +55,7 @@ public struct PaginationLoadMore<
5555
}
5656

5757
extension LoadableView {
58-
fileprivate typealias AppearAction = () -> Void
58+
fileprivate typealias AppearAction = @MainActor @Sendable () -> Void
5959
fileprivate init<ErrorView: View>(
6060
_ store: LoadableStore<Request, State, Action>,
6161
@ViewBuilder onError: @escaping (any Error, @escaping AppearAction) -> ErrorView,

0 commit comments

Comments
 (0)