Skip to content

Commit c95f74a

Browse files
committed
Deprecate Store.publisherScope (#758)
1 parent 4510dac commit c95f74a

File tree

4 files changed

+66
-110
lines changed

4 files changed

+66
-110
lines changed

Sources/ComposableArchitecture/Internal/Deprecations.swift

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,70 @@ import SwiftUI
55

66
// NB: Deprecated after 0.27.1:
77

8+
extension Store {
9+
/// Scopes the store to a producer of stores of more local state and local actions.
10+
///
11+
/// - Parameters:
12+
/// - toLocalState: A function that transforms a producer of `State` into a producer of
13+
/// `LocalState`.
14+
/// - fromLocalAction: A function that transforms `LocalAction` into `Action`.
15+
/// - Returns: A producer of stores with its domain (state and action) transformed.
16+
@available(
17+
*, deprecated,
18+
message:
19+
"If you use this method, please open a discussion on GitHub and let us know how: https://github.com/pointfreeco/swift-composable-architecture/discussions/new"
20+
)
21+
public func producerScope<LocalState, LocalAction>(
22+
state toLocalState: @escaping (Effect<State, Never>) -> Effect<LocalState, Never>,
23+
action fromLocalAction: @escaping (LocalAction) -> Action
24+
) -> Effect<Store<LocalState, LocalAction>, Never> {
25+
26+
func extractLocalState(_ state: State) -> LocalState? {
27+
var localState: LocalState?
28+
_ = toLocalState(Effect(value: state))
29+
.startWithValues { localState = $0 }
30+
return localState
31+
}
32+
33+
return toLocalState(self.producer)
34+
.map { localState in
35+
let localStore = Store<LocalState, LocalAction>(
36+
initialState: localState,
37+
reducer: .init { localState, localAction, _ in
38+
self.send(fromLocalAction(localAction))
39+
localState = extractLocalState(self.state) ?? localState
40+
return .none
41+
},
42+
environment: ()
43+
)
44+
localStore.parentDisposable = self.producer.startWithValues {
45+
[weak localStore] state in
46+
guard let localStore = localStore else { return }
47+
localStore.state = extractLocalState(state) ?? localStore.state
48+
}
49+
return localStore
50+
}
51+
}
52+
53+
/// Scopes the store to a producer of stores of more local state and local actions.
54+
///
55+
/// - Parameter toLocalState: A function that transforms a producer of `State` into a producer
56+
/// of `LocalState`.
57+
/// - Returns: A producer of stores with its domain (state and action)
58+
/// transformed.
59+
@available(
60+
*, deprecated,
61+
message:
62+
"If you use this method, please open a discussion on GitHub and let us know how: https://github.com/pointfreeco/swift-composable-architecture/discussions/new"
63+
)
64+
public func producerScope<LocalState>(
65+
state toLocalState: @escaping (Effect<State, Never>) -> Effect<LocalState, Never>
66+
) -> Effect<Store<LocalState, Action>, Never> {
67+
self.producerScope(state: toLocalState, action: { $0 })
68+
}
69+
70+
}
71+
872
extension ViewStore {
973
@available(
1074
*, deprecated,

Sources/ComposableArchitecture/Store.swift

Lines changed: 1 addition & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,9 @@ public final class Store<State, Action> {
124124
Property<State>(initial: state, then: self.statePipe.output).producer
125125
}
126126
var effectDisposables: [UUID: Disposable] = [:]
127+
var parentDisposable: Disposable?
127128

128129
private var isSending = false
129-
private var parentDisposable: Disposable?
130130
private let reducer: (inout State, Action) -> Effect<Action, Never>
131131
private var bufferedActions: [Action] = []
132132
#if DEBUG
@@ -325,57 +325,6 @@ public final class Store<State, Action> {
325325
self.scope(state: toLocalState, action: { $0 })
326326
}
327327

328-
/// Scopes the store to a producer of stores of more local state and local actions.
329-
///
330-
/// - Parameters:
331-
/// - toLocalState: A function that transforms a producer of `State` into a producer of
332-
/// `LocalState`.
333-
/// - fromLocalAction: A function that transforms `LocalAction` into `Action`.
334-
/// - Returns: A producer of stores with its domain (state and action) transformed.
335-
public func producerScope<LocalState, LocalAction>(
336-
state toLocalState: @escaping (Effect<State, Never>) -> Effect<LocalState, Never>,
337-
action fromLocalAction: @escaping (LocalAction) -> Action
338-
) -> Effect<Store<LocalState, LocalAction>, Never> {
339-
340-
func extractLocalState(_ state: State) -> LocalState? {
341-
var localState: LocalState?
342-
_ = toLocalState(Effect(value: state))
343-
.startWithValues { localState = $0 }
344-
return localState
345-
}
346-
347-
return toLocalState(self.producer)
348-
.map { localState in
349-
let localStore = Store<LocalState, LocalAction>(
350-
initialState: localState,
351-
reducer: .init { localState, localAction, _ in
352-
self.send(fromLocalAction(localAction))
353-
localState = extractLocalState(self.state) ?? localState
354-
return .none
355-
},
356-
environment: ()
357-
)
358-
localStore.parentDisposable = self.producer.startWithValues {
359-
[weak localStore] state in
360-
guard let localStore = localStore else { return }
361-
localStore.state = extractLocalState(state) ?? localStore.state
362-
}
363-
return localStore
364-
}
365-
}
366-
367-
/// Scopes the store to a producer of stores of more local state and local actions.
368-
///
369-
/// - Parameter toLocalState: A function that transforms a producer of `State` into a producer
370-
/// of `LocalState`.
371-
/// - Returns: A producer of stores with its domain (state and action)
372-
/// transformed.
373-
public func producerScope<LocalState>(
374-
state toLocalState: @escaping (Effect<State, Never>) -> Effect<LocalState, Never>
375-
) -> Effect<Store<LocalState, Action>, Never> {
376-
self.producerScope(state: toLocalState, action: { $0 })
377-
}
378-
379328
func send(_ action: Action, isFromViewStore: Bool = true) {
380329
self.threadCheck(status: .send(action, isFromViewStore: isFromViewStore))
381330

Tests/ComposableArchitectureTests/BindingTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ final class BindingTests: XCTestCase {
3232

3333
viewStore.binding(\.$nested.field).wrappedValue = "Hello"
3434

35-
XCTAssertNoDifference(viewStore.state, .init(nested: .init(field: "Hello!")))
35+
XCTAssertEqual(viewStore.state, .init(nested: .init(field: "Hello!")))
3636
}
3737
}

Tests/ComposableArchitectureTests/StoreTests.swift

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -117,34 +117,6 @@ final class StoreTests: XCTestCase {
117117
XCTAssertEqual(values, [0, 1])
118118
}
119119

120-
func testScopeWithPublisherTransform() {
121-
let counterReducer = Reducer<Int, Int, Void> { state, action, _ in
122-
state = action
123-
return .none
124-
}
125-
let parentStore = Store(initialState: 0, reducer: counterReducer, environment: ())
126-
127-
var outputs: [String] = []
128-
129-
parentStore
130-
.producerScope(state: { $0.map { "\($0)" }.skipRepeats() })
131-
.startWithValues { childStore in
132-
childStore.producer
133-
.startWithValues { outputs.append($0) }
134-
}
135-
136-
parentStore.send(0)
137-
XCTAssertEqual(outputs, ["0"])
138-
parentStore.send(0)
139-
XCTAssertEqual(outputs, ["0"])
140-
parentStore.send(1)
141-
XCTAssertEqual(outputs, ["0", "1"])
142-
parentStore.send(1)
143-
XCTAssertEqual(outputs, ["0", "1"])
144-
parentStore.send(2)
145-
XCTAssertEqual(outputs, ["0", "1", "2"])
146-
}
147-
148120
func testScopeCallCount() {
149121
let counterReducer = Reducer<Int, Void, Void> { state, _, _ in state += 1
150122
return .none
@@ -276,35 +248,6 @@ final class StoreTests: XCTestCase {
276248
XCTAssertEqual(ViewStore(store).state, 100_000)
277249
}
278250

279-
func testPublisherScope() {
280-
let appReducer = Reducer<Int, Bool, Void> { state, action, _ in
281-
state += action ? 1 : 0
282-
return .none
283-
}
284-
285-
let parentStore = Store(initialState: 0, reducer: appReducer, environment: ())
286-
287-
var outputs: [Int] = []
288-
289-
parentStore
290-
.producerScope { $0.skipRepeats() }
291-
.startWithValues { outputs.append($0.state) }
292-
293-
XCTAssertEqual(outputs, [0])
294-
295-
parentStore.send(true)
296-
XCTAssertEqual(outputs, [0, 1])
297-
298-
parentStore.send(false)
299-
XCTAssertEqual(outputs, [0, 1])
300-
parentStore.send(false)
301-
XCTAssertEqual(outputs, [0, 1])
302-
parentStore.send(false)
303-
XCTAssertEqual(outputs, [0, 1])
304-
parentStore.send(false)
305-
XCTAssertEqual(outputs, [0, 1])
306-
}
307-
308251
func testIfLetAfterScope() {
309252
struct AppState {
310253
var count: Int?

0 commit comments

Comments
 (0)