|
38 | 38 | ///
|
39 | 39 | /// For example, given a simple counter reducer:
|
40 | 40 | ///
|
41 |
| - /// ```swift |
42 |
| - /// struct CounterState { |
43 |
| - /// var count = 0 |
44 |
| - /// } |
45 |
| - /// |
46 |
| - /// enum CounterAction: Equatable { |
47 |
| - /// case decrementButtonTapped |
48 |
| - /// case incrementButtonTapped |
49 |
| - /// } |
50 |
| - /// |
51 |
| - /// let counterReducer = Reducer<CounterState, CounterAction, Void> { state, action, _ in |
52 |
| - /// switch action { |
53 |
| - /// case .decrementButtonTapped: |
54 |
| - /// state.count -= 1 |
55 |
| - /// return .none |
56 |
| - /// |
57 |
| - /// case .incrementButtonTapped: |
58 |
| - /// state.count += 1 |
59 |
| - /// return .none |
60 |
| - /// } |
61 |
| - /// } |
62 |
| - /// ``` |
| 41 | + /// ```swift |
| 42 | + /// struct CounterState { |
| 43 | + /// var count = 0 |
| 44 | + /// } |
| 45 | + /// |
| 46 | + /// enum CounterAction: Equatable { |
| 47 | + /// case decrementButtonTapped |
| 48 | + /// case incrementButtonTapped |
| 49 | + /// } |
| 50 | + /// |
| 51 | + /// let counterReducer = Reducer<CounterState, CounterAction, Void> { state, action, _ in |
| 52 | + /// switch action { |
| 53 | + /// case .decrementButtonTapped: |
| 54 | + /// state.count -= 1 |
| 55 | + /// return .none |
| 56 | + /// |
| 57 | + /// case .incrementButtonTapped: |
| 58 | + /// state.count += 1 |
| 59 | + /// return .none |
| 60 | + /// } |
| 61 | + /// } |
| 62 | + /// ``` |
63 | 63 | ///
|
64 | 64 | /// One can assert against its behavior over time:
|
65 | 65 | ///
|
66 |
| - /// ```swift |
67 |
| - /// class CounterTests: XCTestCase { |
68 |
| - /// func testCounter() { |
69 |
| - /// let store = TestStore( |
70 |
| - /// initialState: .init(count: 0), // GIVEN counter state of 0 |
71 |
| - /// reducer: counterReducer, |
72 |
| - /// environment: () |
73 |
| - /// ) |
74 |
| - /// store.send(.incrementButtonTapped) { // WHEN the increment button is tapped |
75 |
| - /// $0.count = 1 // THEN the count should be 1 |
76 |
| - /// } |
77 |
| - /// } |
| 66 | + /// ```swift |
| 67 | + /// class CounterTests: XCTestCase { |
| 68 | + /// func testCounter() { |
| 69 | + /// let store = TestStore( |
| 70 | + /// initialState: .init(count: 0), // GIVEN counter state of 0 |
| 71 | + /// reducer: counterReducer, |
| 72 | + /// environment: () |
| 73 | + /// ) |
| 74 | + /// store.send(.incrementButtonTapped) { // WHEN the increment button is tapped |
| 75 | + /// $0.count = 1 // THEN the count should be 1 |
78 | 76 | /// }
|
79 |
| - /// ``` |
| 77 | + /// } |
| 78 | + /// } |
| 79 | + /// ``` |
80 | 80 | ///
|
81 | 81 | /// Note that in the trailing closure of `.send(.incrementButtonTapped)` we are given a single
|
82 | 82 | /// mutable value of the state before the action was sent, and it is our job to mutate the value
|
|
86 | 86 | /// ``Effect/debounce(id:for:scheduler:options:)`` operator to wait for the user to stop typing
|
87 | 87 | /// before making a network request:
|
88 | 88 | ///
|
89 |
| - /// ```swift |
90 |
| - /// struct SearchState: Equatable { |
91 |
| - /// var query = "" |
92 |
| - /// var results: [String] = [] |
93 |
| - /// } |
| 89 | + /// ```swift |
| 90 | + /// struct SearchState: Equatable { |
| 91 | + /// var query = "" |
| 92 | + /// var results: [String] = [] |
| 93 | + /// } |
94 | 94 | ///
|
95 |
| - /// enum SearchAction: Equatable { |
96 |
| - /// case queryChanged(String) |
97 |
| - /// case response([String]) |
98 |
| - /// } |
| 95 | + /// enum SearchAction: Equatable { |
| 96 | + /// case queryChanged(String) |
| 97 | + /// case response([String]) |
| 98 | + /// } |
99 | 99 | ///
|
100 |
| - /// struct SearchEnvironment { |
| 100 | + /// struct SearchEnvironment { |
101 | 101 | /// var mainQueue: DateScheduler
|
102 |
| - /// var request: (String) -> Effect<[String], Never> |
103 |
| - /// } |
| 102 | + /// var request: (String) -> Effect<[String], Never> |
| 103 | + /// } |
104 | 104 | ///
|
105 |
| - /// let searchReducer = Reducer<SearchState, SearchAction, SearchEnvironment> { |
106 |
| - /// state, action, environment in |
| 105 | + /// let searchReducer = Reducer<SearchState, SearchAction, SearchEnvironment> { |
| 106 | + /// state, action, environment in |
107 | 107 | ///
|
108 |
| - /// struct SearchId: Hashable {} |
| 108 | + /// struct SearchId: Hashable {} |
109 | 109 | ///
|
110 |
| - /// switch action { |
111 |
| - /// case let .queryChanged(query): |
112 |
| - /// state.query = query |
113 |
| - /// return environment.request(self.query) |
| 110 | + /// switch action { |
| 111 | + /// case let .queryChanged(query): |
| 112 | + /// state.query = query |
| 113 | + /// return environment.request(self.query) |
114 | 114 | /// .debounce(id: SearchId(), interval: 0.5, scheduler: environment.mainQueue)
|
115 | 115 | ///
|
116 |
| - /// case let .response(results): |
117 |
| - /// state.results = results |
118 |
| - /// return .none |
119 |
| - /// } |
| 116 | + /// case let .response(results): |
| 117 | + /// state.results = results |
| 118 | + /// return .none |
120 | 119 | /// }
|
| 120 | + /// } |
121 | 121 | /// ```
|
122 | 122 | ///
|
123 | 123 | /// It can be fully tested by controlling the environment's scheduler and effect:
|
124 | 124 | ///
|
125 |
| - /// ```swift |
126 |
| - /// // Create a test dispatch scheduler to control the timing of effects |
| 125 | + /// ```swift |
| 126 | + /// // Create a test dispatch scheduler to control the timing of effects |
127 | 127 | /// let scheduler = TestScheduler()
|
128 | 128 | ///
|
129 |
| - /// let store = TestStore( |
130 |
| - /// initialState: SearchState(), |
131 |
| - /// reducer: searchReducer, |
132 |
| - /// environment: SearchEnvironment( |
| 129 | + /// let store = TestStore( |
| 130 | + /// initialState: SearchState(), |
| 131 | + /// reducer: searchReducer, |
| 132 | + /// environment: SearchEnvironment( |
133 | 133 | /// mainQueue: scheduler,
|
134 |
| - /// // Simulate a search response with one item |
135 |
| - /// request: { _ in Effect(value: ["Composable Architecture"]) } |
136 |
| - /// ) |
137 |
| - /// ) |
138 |
| - /// |
139 |
| - /// // Change the query |
140 |
| - /// store.send(.searchFieldChanged("c") { |
141 |
| - /// // Assert that state updates accordingly |
142 |
| - /// $0.query = "c" |
143 |
| - /// } |
144 |
| - /// |
145 |
| - /// // Advance the scheduler by a period shorter than the debounce |
| 134 | + /// // Simulate a search response with one item |
| 135 | + /// request: { _ in Effect(value: ["Composable Architecture"]) } |
| 136 | + /// ) |
| 137 | + /// ) |
| 138 | + /// |
| 139 | + /// // Change the query |
| 140 | + /// store.send(.searchFieldChanged("c") { |
| 141 | + /// // Assert that state updates accordingly |
| 142 | + /// $0.query = "c" |
| 143 | + /// } |
| 144 | + /// |
| 145 | + /// // Advance the scheduler by a period shorter than the debounce |
146 | 146 | /// scheduler.advance(by: .millseconds(250))
|
147 | 147 | ///
|
148 |
| - /// // Change the query again |
149 |
| - /// store.send(.searchFieldChanged("co") { |
150 |
| - /// $0.query = "co" |
151 |
| - /// } |
| 148 | + /// // Change the query again |
| 149 | + /// store.send(.searchFieldChanged("co") { |
| 150 | + /// $0.query = "co" |
| 151 | + /// } |
152 | 152 | ///
|
153 |
| - /// // Advance the scheduler by a period shorter than the debounce |
| 153 | + /// // Advance the scheduler by a period shorter than the debounce |
154 | 154 | /// scheduler.advance(by: .millseconds(250))
|
155 |
| - /// // Advance the scheduler to the debounce |
| 155 | + /// // Advance the scheduler to the debounce |
156 | 156 | /// scheduler.advance(by: .millseconds(250))
|
157 | 157 | ///
|
158 |
| - /// // Assert that the expected response is received |
159 |
| - /// store.receive(.response(["Composable Architecture"])) { |
160 |
| - /// // Assert that state updates accordingly |
161 |
| - /// $0.results = ["Composable Architecture"] |
162 |
| - /// } |
163 |
| - /// ``` |
| 158 | + /// // Assert that the expected response is received |
| 159 | + /// store.receive(.response(["Composable Architecture"])) { |
| 160 | + /// // Assert that state updates accordingly |
| 161 | + /// $0.results = ["Composable Architecture"] |
| 162 | + /// } |
| 163 | + /// ``` |
164 | 164 | ///
|
165 | 165 | /// This test is proving that the debounced network requests are correctly canceled when we do not
|
166 | 166 | /// wait longer than the 0.5 seconds, because if it wasn't and it delivered an action when we did
|
|
0 commit comments