Skip to content

Commit 428ac22

Browse files
committed
Clarify view store thread safety (#692)
* Update ViewStore.swift * Update ViewStore.swift * wip * Update ViewStore.swift
1 parent 1d9f245 commit 428ac22

File tree

1 file changed

+137
-137
lines changed

1 file changed

+137
-137
lines changed

Sources/ComposableArchitecture/ViewStore.swift

Lines changed: 137 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import ReactiveSwift
22

33
#if canImport(Combine)
4-
import Combine
4+
import Combine
55
#endif
66
#if canImport(SwiftUI)
7-
import SwiftUI
7+
import SwiftUI
88
#endif
99

1010
/// A ``ViewStore`` is an object that can observe state changes and send actions. They are most
@@ -52,22 +52,22 @@ import ReactiveSwift
5252
///
5353
/// ### Thread safety
5454
///
55-
/// The ``ViewStore`` class is not thread-safe, and all interactions with it must happen on the main
56-
/// thread. See the documentation of the ``Store`` class for more information why this decision was
57-
/// made.
55+
/// The ``ViewStore`` class is not thread-safe, and all interactions with it (and the store it was
56+
/// derived from) must happen on the same thread. Further, for SwiftUI applications, all
57+
/// interactions must happen on the _main_ thread. See the documentation of the ``Store`` class for
58+
/// more information as to why this decision was made.
5859
///
5960
/// ### ViewStore object lifetime
6061
///
6162
/// You must always keep a strong reference to any ``ViewStore`` that you create to prevent it from
6263
/// being immediately deallocated, and thereby preventing its ``produced`` ``StoreProducer`` from
6364
/// emiting any more state updates. This is primarly an issue when using UIKit, as the SwiftUI
6465
/// ``WithViewStore`` helper ensures that the ``ViewStore`` is retained.
65-
///
6666
@dynamicMemberLookup
6767
public final class ViewStore<State, Action> {
6868
#if canImport(Combine)
6969
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
70-
public private(set) lazy var objectWillChange = ObservableObjectPublisher()
70+
public private(set) lazy var objectWillChange = ObservableObjectPublisher()
7171
#endif
7272

7373
private let _send: (Action) -> Void
@@ -94,12 +94,12 @@ public final class ViewStore<State, Action> {
9494
guard let self = self else { return }
9595
#if canImport(Combine)
9696
if #available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) {
97-
self.objectWillChange.send()
98-
}
97+
self.objectWillChange.send()
98+
}
9999
#endif
100100
self._state = $0
101101
self.statePipe.input.send(value: $0)
102-
}
102+
}
103103
}
104104

105105
/// A producer of state.
@@ -131,145 +131,145 @@ public final class ViewStore<State, Action> {
131131
}
132132

133133
#if canImport(SwiftUI)
134-
/// Derives a binding from the store that prevents direct writes to state and instead sends
135-
/// actions to the store.
136-
///
137-
/// The method is useful for dealing with SwiftUI components that work with two-way `Binding`s
138-
/// since the ``Store`` does not allow directly writing its state; it only allows reading state
139-
/// and sending actions.
140-
///
141-
/// For example, a text field binding can be created like this:
142-
///
143-
/// ```swift
144-
/// struct State { var name = "" }
145-
/// enum Action { case nameChanged(String) }
146-
///
147-
/// TextField(
148-
/// "Enter name",
149-
/// text: viewStore.binding(
150-
/// get: { $0.name },
151-
/// send: { Action.nameChanged($0) }
152-
/// )
153-
/// )
154-
/// ```
155-
///
156-
/// - Parameters:
157-
/// - get: A function to get the state for the binding from the view
158-
/// store's full state.
159-
/// - localStateToViewAction: A function that transforms the binding's value
160-
/// into an action that can be sent to the store.
161-
/// - Returns: A binding.
134+
/// Derives a binding from the store that prevents direct writes to state and instead sends
135+
/// actions to the store.
136+
///
137+
/// The method is useful for dealing with SwiftUI components that work with two-way `Binding`s
138+
/// since the ``Store`` does not allow directly writing its state; it only allows reading state
139+
/// and sending actions.
140+
///
141+
/// For example, a text field binding can be created like this:
142+
///
143+
/// ```swift
144+
/// struct State { var name = "" }
145+
/// enum Action { case nameChanged(String) }
146+
///
147+
/// TextField(
148+
/// "Enter name",
149+
/// text: viewStore.binding(
150+
/// get: { $0.name },
151+
/// send: { Action.nameChanged($0) }
152+
/// )
153+
/// )
154+
/// ```
155+
///
156+
/// - Parameters:
157+
/// - get: A function to get the state for the binding from the view
158+
/// store's full state.
159+
/// - localStateToViewAction: A function that transforms the binding's value
160+
/// into an action that can be sent to the store.
161+
/// - Returns: A binding.
162162
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
163-
public func binding<LocalState>(
164-
get: @escaping (State) -> LocalState,
165-
send localStateToViewAction: @escaping (LocalState) -> Action
166-
) -> Binding<LocalState> {
167-
Binding(
163+
public func binding<LocalState>(
164+
get: @escaping (State) -> LocalState,
165+
send localStateToViewAction: @escaping (LocalState) -> Action
166+
) -> Binding<LocalState> {
167+
Binding(
168168
get: { get(self._state) },
169-
set: { newLocalState, transaction in
170-
if transaction.animation != nil {
171-
withTransaction(transaction) {
172-
self.send(localStateToViewAction(newLocalState))
173-
}
174-
} else {
169+
set: { newLocalState, transaction in
170+
if transaction.animation != nil {
171+
withTransaction(transaction) {
175172
self.send(localStateToViewAction(newLocalState))
176173
}
174+
} else {
175+
self.send(localStateToViewAction(newLocalState))
177176
}
178-
)
179-
}
177+
}
178+
)
179+
}
180180

181-
/// Derives a binding from the store that prevents direct writes to state and instead sends
182-
/// actions to the store.
183-
///
184-
/// The method is useful for dealing with SwiftUI components that work with two-way `Binding`s
185-
/// since the ``Store`` does not allow directly writing its state; it only allows reading state
186-
/// and sending actions.
187-
///
188-
/// For example, an alert binding can be dealt with like this:
189-
///
190-
/// ```swift
191-
/// struct State { var alert: String? }
192-
/// enum Action { case alertDismissed }
193-
///
194-
/// .alert(
195-
/// item: self.store.binding(
196-
/// get: { $0.alert },
197-
/// send: .alertDismissed
198-
/// )
199-
/// ) { alert in Alert(title: Text(alert.message)) }
200-
/// ```
201-
///
202-
/// - Parameters:
203-
/// - get: A function to get the state for the binding from the view store's full state.
204-
/// - action: The action to send when the binding is written to.
205-
/// - Returns: A binding.
181+
/// Derives a binding from the store that prevents direct writes to state and instead sends
182+
/// actions to the store.
183+
///
184+
/// The method is useful for dealing with SwiftUI components that work with two-way `Binding`s
185+
/// since the ``Store`` does not allow directly writing its state; it only allows reading state
186+
/// and sending actions.
187+
///
188+
/// For example, an alert binding can be dealt with like this:
189+
///
190+
/// ```swift
191+
/// struct State { var alert: String? }
192+
/// enum Action { case alertDismissed }
193+
///
194+
/// .alert(
195+
/// item: self.store.binding(
196+
/// get: { $0.alert },
197+
/// send: .alertDismissed
198+
/// )
199+
/// ) { alert in Alert(title: Text(alert.message)) }
200+
/// ```
201+
///
202+
/// - Parameters:
203+
/// - get: A function to get the state for the binding from the view store's full state.
204+
/// - action: The action to send when the binding is written to.
205+
/// - Returns: A binding.
206206
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
207-
public func binding<LocalState>(
208-
get: @escaping (State) -> LocalState,
209-
send action: Action
210-
) -> Binding<LocalState> {
211-
self.binding(get: get, send: { _ in action })
212-
}
207+
public func binding<LocalState>(
208+
get: @escaping (State) -> LocalState,
209+
send action: Action
210+
) -> Binding<LocalState> {
211+
self.binding(get: get, send: { _ in action })
212+
}
213213

214-
/// Derives a binding from the store that prevents direct writes to state and instead sends
215-
/// actions to the store.
216-
///
217-
/// The method is useful for dealing with SwiftUI components that work with two-way `Binding`s
218-
/// since the ``Store`` does not allow directly writing its state; it only allows reading state
219-
/// and sending actions.
220-
///
221-
/// For example, a text field binding can be created like this:
222-
///
223-
/// ```swift
224-
/// typealias State = String
225-
/// enum Action { case nameChanged(String) }
226-
///
227-
/// TextField(
228-
/// "Enter name",
229-
/// text: viewStore.binding(
230-
/// send: { Action.nameChanged($0) }
231-
/// )
232-
/// )
233-
/// ```
234-
///
235-
/// - Parameters:
236-
/// - localStateToViewAction: A function that transforms the binding's value
237-
/// into an action that can be sent to the store.
238-
/// - Returns: A binding.
214+
/// Derives a binding from the store that prevents direct writes to state and instead sends
215+
/// actions to the store.
216+
///
217+
/// The method is useful for dealing with SwiftUI components that work with two-way `Binding`s
218+
/// since the ``Store`` does not allow directly writing its state; it only allows reading state
219+
/// and sending actions.
220+
///
221+
/// For example, a text field binding can be created like this:
222+
///
223+
/// ```swift
224+
/// typealias State = String
225+
/// enum Action { case nameChanged(String) }
226+
///
227+
/// TextField(
228+
/// "Enter name",
229+
/// text: viewStore.binding(
230+
/// send: { Action.nameChanged($0) }
231+
/// )
232+
/// )
233+
/// ```
234+
///
235+
/// - Parameters:
236+
/// - localStateToViewAction: A function that transforms the binding's value
237+
/// into an action that can be sent to the store.
238+
/// - Returns: A binding.
239239
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
240-
public func binding(
241-
send localStateToViewAction: @escaping (State) -> Action
242-
) -> Binding<State> {
243-
self.binding(get: { $0 }, send: localStateToViewAction)
244-
}
240+
public func binding(
241+
send localStateToViewAction: @escaping (State) -> Action
242+
) -> Binding<State> {
243+
self.binding(get: { $0 }, send: localStateToViewAction)
244+
}
245245

246-
/// Derives a binding from the store that prevents direct writes to state and instead sends
247-
/// actions to the store.
248-
///
249-
/// The method is useful for dealing with SwiftUI components that work with two-way `Binding`s
250-
/// since the ``Store`` does not allow directly writing its state; it only allows reading state
251-
/// and sending actions.
252-
///
253-
/// For example, an alert binding can be dealt with like this:
254-
///
255-
/// ```swift
256-
/// typealias State = String
257-
/// enum Action { case alertDismissed }
258-
///
259-
/// .alert(
246+
/// Derives a binding from the store that prevents direct writes to state and instead sends
247+
/// actions to the store.
248+
///
249+
/// The method is useful for dealing with SwiftUI components that work with two-way `Binding`s
250+
/// since the ``Store`` does not allow directly writing its state; it only allows reading state
251+
/// and sending actions.
252+
///
253+
/// For example, an alert binding can be dealt with like this:
254+
///
255+
/// ```swift
256+
/// typealias State = String
257+
/// enum Action { case alertDismissed }
258+
///
259+
/// .alert(
260260
/// item: self.store.binding(
261-
/// send: .alertDismissed
262-
/// )
263-
/// ) { title in Alert(title: Text(title)) }
264-
/// ```
265-
///
266-
/// - Parameters:
267-
/// - action: The action to send when the binding is written to.
268-
/// - Returns: A binding.
261+
/// send: .alertDismissed
262+
/// )
263+
/// ) { title in Alert(title: Text(title)) }
264+
/// ```
265+
///
266+
/// - Parameters:
267+
/// - action: The action to send when the binding is written to.
268+
/// - Returns: A binding.
269269
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
270-
public func binding(send action: Action) -> Binding<State> {
271-
self.binding(send: { _ in action })
272-
}
270+
public func binding(send action: Action) -> Binding<State> {
271+
self.binding(send: { _ in action })
272+
}
273273
#endif
274274

275275
deinit {

0 commit comments

Comments
 (0)