Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbd49bf4

Browse files
Rewrite scope strategy (#43)
1 parent3670253 commitbd49bf4

File tree

3 files changed

+111
-80
lines changed

3 files changed

+111
-80
lines changed

‎Sources/Store.swift‎

Lines changed: 67 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ public class Store<StoreState: State, StoreController: Cancellable>: Publisher {
1212
didSet{
1313
queue.sync{
1414
if state!= oldValue{
15-
stateCurrentValueSubject.send(state)
16-
statePassthroughSubject.send(state)
15+
stateSubject.send(state)
1716
}
1817
}
1918
}
@@ -22,15 +21,12 @@ public class Store<StoreState: State, StoreController: Cancellable>: Publisher {
2221

2322
publicinit(_ state:StoreState,
2423
dispatcher:Dispatcher,
25-
storeController:StoreController,
26-
defaultPublisherMode:DefaultPublisherMode=.currentValue){
24+
storeController:StoreController){
2725
self.initialState= state
2826
self.dispatcher= dispatcher
29-
self.stateCurrentValueSubject=.init(state)
30-
self.statePassthroughSubject=.init()
27+
self.stateSubject=.init(state)
3128
self.storeController= storeController
3229
self.state= state
33-
self.defaultPublisherMode= defaultPublisherMode
3430
}
3531

3632
/**
@@ -57,8 +53,7 @@ public class Store<StoreState: State, StoreController: Cancellable>: Publisher {
5753
}
5854

5955
publicfunc replayOnce(){
60-
stateCurrentValueSubject.send(state)
61-
statePassthroughSubject.send(state)
56+
stateSubject.send(state)
6257

6358
dispatcher.stateWasReplayed(state: state)
6459
}
@@ -71,56 +66,85 @@ public class Store<StoreState: State, StoreController: Cancellable>: Publisher {
7166
publisher.receive(subscriber: subscriber)
7267
}
7368

74-
publicvarpublisher:StorePublisher{
75-
switch defaultPublisherMode{
76-
case.passthrough:
77-
return passthroughPublisher
78-
79-
case.currentValue:
80-
return currentValuePublisher
81-
}
82-
}
83-
84-
publicvarpassthroughPublisher:StorePublisher{
85-
.init(subject: statePassthroughSubject)
86-
}
87-
88-
publicvarcurrentValuePublisher:StorePublisher{
89-
.init(subject: stateCurrentValueSubject)
69+
publicvarpublisher:Publishers.StoreStatePublisher<StoreState>{
70+
.init(upstream: stateSubject)
9071
}
9172

9273
/// Scope a task from the state and receive only new updated since subscription.
93-
publicfunc scope<T:Taskable&Equatable>(_ transform:@escaping(StoreState)->T)->AnyPublisher<T,Failure>{
94-
passthroughPublisher
95-
.map(transform)
96-
.removeDuplicates()
97-
.eraseToAnyPublisher()
74+
publicfunc scope<T:Taskable>(_ transform:@escaping(StoreState)->T)->Publishers.StoreScopePublisher<T>{
75+
Publishers.StoreScopePublisher(upstream: stateSubject.map(transform),
76+
initialValue:transform(state))
9877
}
9978

100-
privatevarstateCurrentValueSubject:CurrentValueSubject<StoreState,Never>
101-
privatevarstatePassthroughSubject:PassthroughSubject<StoreState,Never>
79+
privatevarstateSubject:CurrentValueSubject<StoreState,Never>
10280
privateletqueue=DispatchQueue(label:"atomic state")
103-
privateletdefaultPublisherMode:DefaultPublisherMode
10481
}
10582

106-
publicextensionStore{
107-
enumDefaultPublisherMode{
108-
case passthrough
109-
case currentValue
83+
publicextensionPublishers{
84+
classStoreStatePublisher<StoreState:State>:Publisher{
85+
publictypealiasUpstream=anySubject<StoreState,Never>
86+
publictypealiasOutput=StoreState
87+
publictypealiasFailure=Never
88+
89+
privateletupstream:Upstream
90+
91+
internalinit(upstream:Upstream){
92+
self.upstream= upstream
93+
}
94+
95+
publicfunc receive<S:Subscriber>(subscriber:S)where Failure==S.Failure, Output==S.Input{
96+
upstream.subscribe(subscriber)
97+
}
11098
}
11199

112-
classStorePublisher:Publisher{
113-
publictypealiasOutput=StoreState
100+
structStoreScopePublisher<StoreTask:Taskable>:Publisher{
101+
publictypealiasUpstream=anyPublisher<StoreTask,Never>
102+
publictypealiasOutput=StoreTask
114103
publictypealiasFailure=Never
115104

116-
privatevarsubject:anySubject<StoreState,Never>
105+
privateletupstream:Upstream
106+
privateletinitialValue:StoreTask
117107

118-
internalinit(subject:anySubject<StoreState,Never>){
119-
self.subject= subject
108+
internalinit(upstream:Upstream, initialValue:StoreTask){
109+
self.upstream= upstream
110+
self.initialValue= initialValue
120111
}
121112

122113
publicfunc receive<S:Subscriber>(subscriber:S)where Failure==S.Failure, Output==S.Input{
123-
subject.subscribe(subscriber)
114+
upstream.subscribe(Inner(downstream: subscriber, initialValue: initialValue))
115+
}
116+
}
117+
}
118+
119+
extensionPublishers.StoreScopePublisher{
120+
privateclassInner<Downstream:Subscriber>:Subscriber
121+
where Downstream.Input==Output, Downstream.Failure==Never, Output==StoreTask{
122+
publictypealiasInput=Output
123+
publictypealiasFailure=Never
124+
125+
letcombineIdentifier=CombineIdentifier()
126+
privateletdownstream:Downstream
127+
privatevarlastValue:StoreTask
128+
129+
fileprivateinit(downstream:Downstream, initialValue:StoreTask){
130+
self.downstream= downstream
131+
self.lastValue= initialValue
132+
}
133+
134+
func receive(subscription:Subscription){
135+
downstream.receive(subscription: subscription)
136+
}
137+
138+
func receive(_ input:Output)->Subscribers.Demand{
139+
if input== lastValue{
140+
return.none
141+
}
142+
self.lastValue= input
143+
return downstream.receive(input)
144+
}
145+
146+
func receive(completion:Subscribers.Completion<Failure>){
147+
downstream.receive(completion: completion)
124148
}
125149
}
126150
}

‎Tests/ReducerTests.swift‎

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,11 @@ final class ReducerTests: XCTestCase {
6969
XCTAssertEqual(store.state, initialState)
7070
}
7171

72-
functest_subscribe_state_changes_with_initial_value(){
72+
functest_subscribe_state_changes(){
7373
varcancellables=Set<AnyCancellable>()
7474
letdispatcher=Dispatcher()
7575
letinitialState=TestStateWithOneTask()
76-
letstore=Store<TestStateWithOneTask,TestStoreController>(initialState, dispatcher: dispatcher, storeController:TestStoreController(), defaultPublisherMode:.currentValue)
76+
letstore=Store<TestStateWithOneTask,TestStoreController>(initialState, dispatcher: dispatcher, storeController:TestStoreController())
7777
letexpectation1=XCTestExpectation(description:"Subscription Emits 1")
7878
letexpectation2=XCTestExpectation(description:"Subscription Emits 2")
7979

@@ -97,38 +97,4 @@ final class ReducerTests: XCTestCase {
9797
dispatcher.dispatch(TestAction(counter:2))
9898
wait(for:[expectation1, expectation2], timeout:5.0)
9999
}
100-
101-
func test_subscribe_state_changes_without_initial_value(){
102-
varcancellables=Set<AnyCancellable>()
103-
letdispatcher=Dispatcher()
104-
letinitialState=TestStateWithOneTask()
105-
letstore=Store<TestStateWithOneTask,TestStoreController>(initialState, dispatcher: dispatcher, storeController:TestStoreController(), defaultPublisherMode:.passthrough)
106-
letexpectation=XCTestExpectation(description:"Subscription Emits")
107-
108-
store
109-
.reducerGroup()
110-
.store(in:&cancellables)
111-
112-
dispatcher.dispatch(TestAction(counter:1))
113-
114-
DispatchQueue.main.asyncAfter(deadline:.now()+1){
115-
// Only gets the action with counter == 2.
116-
store
117-
.map(\.counter)
118-
.sink{ counterin
119-
if counter==1{
120-
XCTFail("counter == 1 should not be emmited because this is a stateless subscription")
121-
}
122-
if counter==2{
123-
expectation.fulfill()
124-
}
125-
}
126-
.store(in:&cancellables)
127-
128-
// Send action with counter == 2, this action should be caught by the two subscriptions
129-
dispatcher.dispatch(TestAction(counter:2))
130-
}
131-
132-
wait(for:[expectation], timeout:5.0)
133-
}
134100
}

‎Tests/StoreTests.swift‎

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Combine
33
import XCTest
44

55
classStoreTests:XCTestCase{
6-
functest_scope(){
6+
functest_scope_with_initial_state(){
77
varcancellables=Set<AnyCancellable>()
88
letexpectation=XCTestExpectation(description:"Scope usage check")
99
expectation.expectedFulfillmentCount=2
@@ -37,4 +37,45 @@ class StoreTests: XCTestCase {
3737

3838
XCTAssertTrue(counterValue==2)
3939
}
40+
41+
func test_scope_with_initial_change_after_subscriptions(){
42+
varcancellables=Set<AnyCancellable>()
43+
letexpectation=XCTestExpectation(description:"Scope usage check")
44+
expectation.expectedFulfillmentCount=2
45+
letdispatcher=Dispatcher()
46+
letinitialState=TestStateWithTwoTasks()
47+
letstore=Store<TestStateWithTwoTasks,TestStoreController>(initialState, dispatcher: dispatcher, storeController:TestStoreController())
48+
49+
varcounterValue=0
50+
// SCOPING....
51+
store
52+
.scope{ $0.testTask1}
53+
.sink{ _in
54+
expectation.fulfill()
55+
counterValue+=1
56+
}
57+
.store(in:&cancellables)
58+
59+
Thread.sleep(forTimeInterval:1)
60+
61+
// THIS NOT PASS: change task2
62+
store.state=TestStateWithTwoTasks(testTask1: store.state.testTask1,
63+
testTask2:.success(10))
64+
65+
// THIS PASSES: change task1
66+
store.state=TestStateWithTwoTasks(testTask1:.success(6),
67+
testTask2: store.state.testTask2)
68+
69+
// THIS NOT PASS: change task2
70+
store.state=TestStateWithTwoTasks(testTask1: store.state.testTask1,
71+
testTask2:.success(2))
72+
73+
// THIS PASSES: change task1
74+
store.state=TestStateWithTwoTasks(testTask1:.success(7),
75+
testTask2: store.state.testTask2)
76+
77+
wait(for:[expectation], timeout:5.0)
78+
79+
XCTAssertTrue(counterValue==2)
80+
}
4081
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp