Skip to content

Commit 632ad0f

Browse files
committed
Swift 5.7 fixes (#1089)
* Fix Swift 5.7 warnings * Fix Swift 5.7 threading issue
1 parent c43f904 commit 632ad0f

File tree

4 files changed

+112
-114
lines changed

4 files changed

+112
-114
lines changed

Examples/CaseStudies/SwiftUICaseStudies/01-GettingStarted-FocusState.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
}
7676

7777
extension View {
78-
func synchronize<Value: Equatable>(
78+
func synchronize<Value>(
7979
_ first: Binding<Value>,
8080
_ second: FocusState<Value>.Binding
8181
) -> some View {

Sources/ComposableArchitecture/Internal/Deprecations.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,6 @@ extension ForEachStore {
635635
)
636636
where
637637
Data == [EachState],
638-
EachContent: View,
639638
Content == WithViewStore<
640639
[ID], (Data.Index, EachAction), ForEach<[(offset: Int, element: ID)], ID, EachContent>
641640
>
@@ -663,7 +662,6 @@ extension ForEachStore {
663662
)
664663
where
665664
Data == [EachState],
666-
EachContent: View,
667665
Content == WithViewStore<
668666
[ID], (Data.Index, EachAction), ForEach<[(offset: Int, element: ID)], ID, EachContent>
669667
>,
Lines changed: 110 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,122 @@
11
#if canImport(SwiftUI)
2-
import OrderedCollections
3-
import SwiftUI
2+
import OrderedCollections
3+
import SwiftUI
44

5-
/// A Composable Architecture-friendly wrapper around `ForEach` that simplifies working with
6-
/// collections of state.
7-
///
8-
/// ``ForEachStore`` loops over a store's collection with a store scoped to the domain of each
9-
/// element. This allows you to extract and modularize an element's view and avoid concerns around
10-
/// collection index math and parent-child store communication.
11-
///
12-
/// For example, a todos app may define the domain and logic associated with an individual todo:
13-
///
14-
/// ```swift
15-
/// struct TodoState: Equatable, Identifiable {
16-
/// let id: UUID
17-
/// var description = ""
18-
/// var isComplete = false
19-
/// }
20-
/// enum TodoAction {
21-
/// case isCompleteToggled(Bool)
22-
/// case descriptionChanged(String)
23-
/// }
24-
/// struct TodoEnvironment {}
25-
/// let todoReducer = Reducer<TodoState, TodoAction, TodoEnvironment { ... }
26-
/// ```
27-
///
28-
/// As well as a view with a domain-specific store:
29-
///
30-
/// ```swift
31-
/// struct TodoView: View {
32-
/// let store: Store<TodoState, TodoAction>
33-
/// var body: some View { ... }
34-
/// }
35-
/// ```
36-
///
37-
/// For a parent domain to work with a collection of todos, it can hold onto this collection in
38-
/// state:
39-
///
40-
/// ```swift
41-
/// struct AppState: Equatable {
42-
/// var todos: IdentifiedArrayOf<TodoState> = []
43-
/// }
44-
/// ```
45-
///
46-
/// Define a case to handle actions sent to the child domain:
47-
///
48-
/// ```swift
49-
/// enum AppAction {
50-
/// case todo(id: TodoState.ID, action: TodoAction)
51-
/// }
52-
/// ```
53-
///
54-
/// Enhance its reducer using ``Reducer/forEach(state:action:environment:file:line:)-gvte``:
55-
///
56-
/// ```swift
57-
/// let appReducer = todoReducer.forEach(
58-
/// state: \.todos,
59-
/// action: /AppAction.todo(id:action:),
60-
/// environment: { _ in TodoEnvironment() }
61-
/// )
62-
/// ```
63-
///
64-
/// And finally render a list of `TodoView`s using ``ForEachStore``:
65-
///
66-
/// ```swift
67-
/// ForEachStore(
68-
/// self.store.scope(state: \.todos, AppAction.todo(id:action:))
69-
/// ) { todoStore in
70-
/// TodoView(store: todoStore)
71-
/// }
72-
/// ```
73-
///
74-
public struct ForEachStore<EachState, EachAction, Data, ID, Content>: DynamicViewContent
75-
where Data: Collection, ID: Hashable, Content: View {
76-
public let data: Data
77-
let content: () -> Content
5+
/// A Composable Architecture-friendly wrapper around `ForEach` that simplifies working with
6+
/// collections of state.
7+
///
8+
/// ``ForEachStore`` loops over a store's collection with a store scoped to the domain of each
9+
/// element. This allows you to extract and modularize an element's view and avoid concerns around
10+
/// collection index math and parent-child store communication.
11+
///
12+
/// For example, a todos app may define the domain and logic associated with an individual todo:
13+
///
14+
/// ```swift
15+
/// struct TodoState: Equatable, Identifiable {
16+
/// let id: UUID
17+
/// var description = ""
18+
/// var isComplete = false
19+
/// }
20+
/// enum TodoAction {
21+
/// case isCompleteToggled(Bool)
22+
/// case descriptionChanged(String)
23+
/// }
24+
/// struct TodoEnvironment {}
25+
/// let todoReducer = Reducer<TodoState, TodoAction, TodoEnvironment { ... }
26+
/// ```
27+
///
28+
/// As well as a view with a domain-specific store:
29+
///
30+
/// ```swift
31+
/// struct TodoView: View {
32+
/// let store: Store<TodoState, TodoAction>
33+
/// var body: some View { ... }
34+
/// }
35+
/// ```
36+
///
37+
/// For a parent domain to work with a collection of todos, it can hold onto this collection in
38+
/// state:
39+
///
40+
/// ```swift
41+
/// struct AppState: Equatable {
42+
/// var todos: IdentifiedArrayOf<TodoState> = []
43+
/// }
44+
/// ```
45+
///
46+
/// Define a case to handle actions sent to the child domain:
47+
///
48+
/// ```swift
49+
/// enum AppAction {
50+
/// case todo(id: TodoState.ID, action: TodoAction)
51+
/// }
52+
/// ```
53+
///
54+
/// Enhance its reducer using ``Reducer/forEach(state:action:environment:file:line:)-gvte``:
55+
///
56+
/// ```swift
57+
/// let appReducer = todoReducer.forEach(
58+
/// state: \.todos,
59+
/// action: /AppAction.todo(id:action:),
60+
/// environment: { _ in TodoEnvironment() }
61+
/// )
62+
/// ```
63+
///
64+
/// And finally render a list of `TodoView`s using ``ForEachStore``:
65+
///
66+
/// ```swift
67+
/// ForEachStore(
68+
/// self.store.scope(state: \.todos, AppAction.todo(id:action:))
69+
/// ) { todoStore in
70+
/// TodoView(store: todoStore)
71+
/// }
72+
/// ```
73+
///
74+
public struct ForEachStore<EachState, EachAction, Data, ID, Content>: DynamicViewContent
75+
where Data: Collection, ID: Hashable, Content: View {
76+
public let data: Data
77+
let content: () -> Content
7878

79-
/// Initializes a structure that computes views on demand from a store on a collection of data and
80-
/// an identified action.
81-
///
82-
/// - Parameters:
83-
/// - store: A store on an identified array of data and an identified action.
84-
/// - content: A function that can generate content given a store of an element.
85-
public init<EachContent>(
86-
_ store: Store<IdentifiedArray<ID, EachState>, (ID, EachAction)>,
87-
@ViewBuilder content: @escaping (Store<EachState, EachAction>) -> EachContent
88-
)
89-
where
90-
EachContent: View,
91-
Data == IdentifiedArray<ID, EachState>,
92-
Content == WithViewStore<
93-
OrderedSet<ID>, (ID, EachAction), ForEach<OrderedSet<ID>, ID, EachContent>
94-
>
95-
{
79+
/// Initializes a structure that computes views on demand from a store on a collection of data and
80+
/// an identified action.
81+
///
82+
/// - Parameters:
83+
/// - store: A store on an identified array of data and an identified action.
84+
/// - content: A function that can generate content given a store of an element.
85+
public init<EachContent>(
86+
_ store: Store<IdentifiedArray<ID, EachState>, (ID, EachAction)>,
87+
@ViewBuilder content: @escaping (Store<EachState, EachAction>) -> EachContent
88+
)
89+
where
90+
Data == IdentifiedArray<ID, EachState>,
91+
Content == WithViewStore<
92+
OrderedSet<ID>, (ID, EachAction), ForEach<OrderedSet<ID>, ID, EachContent>
93+
>
94+
{
9695
self.data = store.state
97-
self.content = {
98-
WithViewStore(store.scope(state: { $0.ids })) { viewStore in
99-
ForEach(viewStore.state, id: \.self) { id -> EachContent in
100-
// NB: We cache elements here to avoid a potential crash where SwiftUI may re-evaluate
101-
// views for elements no longer in the collection.
102-
//
103-
// Feedback filed: https://gist.github.com/stephencelis/cdf85ae8dab437adc998fb0204ed9a6b
96+
self.content = {
97+
WithViewStore(store.scope(state: { $0.ids })) { viewStore in
98+
ForEach(viewStore.state, id: \.self) { id -> EachContent in
99+
// NB: We cache elements here to avoid a potential crash where SwiftUI may re-evaluate
100+
// views for elements no longer in the collection.
101+
//
102+
// Feedback filed: https://gist.github.com/stephencelis/cdf85ae8dab437adc998fb0204ed9a6b
104103
var element = store.state[id: id]!
105-
return content(
106-
store.scope(
107-
state: {
108-
element = $0[id: id] ?? element
109-
return element
110-
},
111-
action: { (id, $0) }
112-
)
104+
return content(
105+
store.scope(
106+
state: {
107+
element = $0[id: id] ?? element
108+
return element
109+
},
110+
action: { (id, $0) }
113111
)
114-
}
112+
)
115113
}
116114
}
117115
}
116+
}
118117

119-
public var body: some View {
120-
self.content()
121-
}
118+
public var body: some View {
119+
self.content()
122120
}
121+
}
123122
#endif

Sources/ComposableArchitecture/ViewStore.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ private struct HashableWrapper<Value>: Hashable {
448448
/// - action: An action.
449449
/// - predicate: A predicate on `State` that determines for how long this method should
450450
/// suspend.
451+
@MainActor
451452
public func send(
452453
_ action: Action,
453454
while predicate: @escaping (State) -> Bool

0 commit comments

Comments
 (0)