@@ -12,27 +12,35 @@ import RxCocoa
12
12
// Taken from RxFeedback repo
13
13
14
14
/**
15
- Control feedback loop that tries to immediatelly perform the latest required effect.
16
-
17
15
* State: State type of the system.
18
- * Control : Subset of state used to control the feedback loop.
16
+ * Query : Subset of state used to control the feedback loop.
19
17
20
- When query result exists (not `nil`), feedback loop is active and it performs effects.
18
+ When `query` returns a value, that value is being passed into `effects` lambda to decide which effects should be performed.
19
+ In case new `query` is different from the previous one, new effects are calculated by using `effects` lambda and then performed.
21
20
22
- When query result is `nil`, feedback loops doesn't perform any effect.
21
+ When ` query` returns `nil`, feedback loops doesn't perform any effect.
23
22
24
- - parameter query: State type of the system
25
- - parameter effects: Control state which is subset of state.
23
+ - parameter query: Part of state that controls feedback loop.
24
+ - parameter areEqual: Part of state that controls feedback loop.
25
+ - parameter effects: Chooses which effects to perform for certain query result.
26
26
- returns: Feedback loop performing the effects.
27
27
*/
28
- public func react< State, Control: Equatable , Event> (
29
- query: @escaping ( State ) -> Control ? ,
30
- effects: @escaping ( Control ) -> Observable < Event >
28
+ public func react< State, Query, Event> (
29
+ query: @escaping ( State ) -> Query ? ,
30
+ areEqual: @escaping ( Query , Query ) -> Bool ,
31
+ effects: @escaping ( Query ) -> Observable < Event >
31
32
) -> ( ObservableSchedulerContext < State > ) -> Observable < Event > {
32
33
return { state in
33
34
return state. map ( query)
34
- . distinctUntilChanged { $0 == $1 }
35
- . flatMapLatest { ( control: Control ? ) -> Observable < Event > in
35
+ . distinctUntilChanged { lhs, rhs in
36
+ switch ( lhs, rhs) {
37
+ case ( . none, . none) : return true
38
+ case ( . none, . some) : return false
39
+ case ( . some, . none) : return false
40
+ case ( . some( let lhs) , . some( let rhs) ) : return areEqual ( lhs, rhs)
41
+ }
42
+ }
43
+ . flatMapLatest { ( control: Query ? ) -> Observable < Event > in
36
44
guard let control = control else {
37
45
return Observable< Event> . empty( )
38
46
}
@@ -44,55 +52,102 @@ public func react<State, Control: Equatable, Event>(
44
52
}
45
53
46
54
/**
47
- Control feedback loop that tries to immediatelly perform the latest required effect.
55
+ * State: State type of the system.
56
+ * Query: Subset of state used to control the feedback loop.
57
+
58
+ When `query` returns a value, that value is being passed into `effects` lambda to decide which effects should be performed.
59
+ In case new `query` is different from the previous one, new effects are calculated by using `effects` lambda and then performed.
60
+
61
+ When `query` returns `nil`, feedback loops doesn't perform any effect.
62
+
63
+ - parameter query: Part of state that controls feedback loop.
64
+ - parameter effects: Chooses which effects to perform for certain query result.
65
+ - returns: Feedback loop performing the effects.
66
+ */
67
+ public func react< State, Query: Equatable , Event> (
68
+ query: @escaping ( State ) -> Query ? ,
69
+ effects: @escaping ( Query ) -> Observable < Event >
70
+ ) -> ( ObservableSchedulerContext < State > ) -> Observable < Event > {
71
+ return react ( query: query, areEqual: { $0 == $1 } , effects: effects)
72
+ }
48
73
74
+ /**
49
75
* State: State type of the system.
50
- * Control : Subset of state used to control the feedback loop.
76
+ * Query : Subset of state used to control the feedback loop.
51
77
52
- When query result exists (not `nil`), feedback loop is active and it performs effects.
78
+ When `query` returns a value, that value is being passed into `effects` lambda to decide which effects should be performed.
79
+ In case new `query` is different from the previous one, new effects are calculated by using `effects` lambda and then performed.
53
80
54
- When query result is `nil`, feedback loops doesn't perform any effect.
81
+ When ` query` returns `nil`, feedback loops doesn't perform any effect.
55
82
56
- - parameter query: State type of the system
57
- - parameter effects: Control state which is subset of state.
83
+ - parameter query: Part of state that controls feedback loop.
84
+ - parameter areEqual: Part of state that controls feedback loop.
85
+ - parameter effects: Chooses which effects to perform for certain query result.
58
86
- returns: Feedback loop performing the effects.
59
87
*/
60
- public func react< State, Control: Equatable , Event> (
61
- query: @escaping ( State ) -> Control ? ,
62
- effects: @escaping ( Control ) -> Signal < Event >
63
- ) -> ( Driver < State > ) -> Signal < Event > {
88
+ public func react< State, Query, Event> (
89
+ query: @escaping ( State ) -> Query ? ,
90
+ areEqual: @escaping ( Query , Query ) -> Bool ,
91
+ effects: @escaping ( Query ) -> Signal < Event >
92
+ ) -> ( Driver < State > ) -> Signal < Event > {
64
93
return { state in
65
94
let observableSchedulerContext = ObservableSchedulerContext < State > (
66
95
source: state. asObservable ( ) ,
67
96
scheduler: Signal < Event > . SharingStrategy. scheduler. async
68
97
)
69
- return react ( query: query, effects: { effects ( $0) . asObservable ( ) } ) ( observableSchedulerContext)
98
+ return react ( query: query, areEqual : areEqual , effects: { effects ( $0) . asObservable ( ) } ) ( observableSchedulerContext)
70
99
. asSignal ( onErrorSignalWith: . empty( ) )
71
100
}
72
101
}
73
102
74
103
/**
75
- Control feedback loop that tries to immediatelly perform the latest required effect.
104
+ * State: State type of the system.
105
+ * Query: Subset of state used to control the feedback loop.
106
+
107
+ When `query` returns a value, that value is being passed into `effects` lambda to decide which effects should be performed.
108
+ In case new `query` is different from the previous one, new effects are calculated by using `effects` lambda and then performed.
76
109
110
+ When `query` returns `nil`, feedback loops doesn't perform any effect.
111
+
112
+ - parameter query: Part of state that controls feedback loop.
113
+ - parameter effects: Chooses which effects to perform for certain query result.
114
+ - returns: Feedback loop performing the effects.
115
+ */
116
+ public func react< State, Query: Equatable , Event> (
117
+ query: @escaping ( State ) -> Query ? ,
118
+ effects: @escaping ( Query ) -> Signal < Event >
119
+ ) -> ( Driver < State > ) -> Signal < Event > {
120
+ return { state in
121
+ let observableSchedulerContext = ObservableSchedulerContext < State > (
122
+ source: state. asObservable ( ) ,
123
+ scheduler: Signal < Event > . SharingStrategy. scheduler. async
124
+ )
125
+ return react ( query: query, effects: { effects ( $0) . asObservable ( ) } ) ( observableSchedulerContext)
126
+ . asSignal ( onErrorSignalWith: . empty( ) )
127
+ }
128
+ }
129
+
130
+ /**
77
131
* State: State type of the system.
78
- * Control : Subset of state used to control the feedback loop.
132
+ * Query : Subset of state used to control the feedback loop.
79
133
80
- When query result exists (not `nil`), feedback loop is active and it performs effects.
134
+ When `query` returns a value, that value is being passed into `effects` lambda to decide which effects should be performed.
135
+ In case new `query` is different from the previous one, new effects are calculated by using `effects` lambda and then performed.
81
136
82
- When query result is `nil`, feedback loops doesn't perform any effect.
137
+ When ` query` returns `nil`, feedback loops doesn't perform any effect.
83
138
84
- - parameter query: State type of the system
85
- - parameter effects: Control state which is subset of state .
139
+ - parameter query: Part of state that controls feedback loop.
140
+ - parameter effects: Chooses which effects to perform for certain query result .
86
141
- returns: Feedback loop performing the effects.
87
142
*/
88
- public func react< State, Control , Event> (
89
- query: @escaping ( State ) -> Control ? ,
90
- effects: @escaping ( Control ) -> Observable < Event >
91
- ) -> ( ObservableSchedulerContext < State > ) -> Observable < Event > {
143
+ public func react< State, Query , Event> (
144
+ query: @escaping ( State ) -> Query ? ,
145
+ effects: @escaping ( Query ) -> Observable < Event >
146
+ ) -> ( ObservableSchedulerContext < State > ) -> Observable < Event > {
92
147
return { state in
93
148
return state. map ( query)
94
149
. distinctUntilChanged { $0 != nil }
95
- . flatMapLatest { ( control: Control ? ) -> Observable < Event > in
150
+ . flatMapLatest { ( control: Query ? ) -> Observable < Event > in
96
151
guard let control = control else {
97
152
return Observable< Event> . empty( )
98
153
}
@@ -104,23 +159,22 @@ public func react<State, Control, Event>(
104
159
}
105
160
106
161
/**
107
- Control feedback loop that tries to immediatelly perform the latest required effect.
108
-
109
162
* State: State type of the system.
110
- * Control : Subset of state used to control the feedback loop.
163
+ * Query : Subset of state used to control the feedback loop.
111
164
112
- When query result exists (not `nil`), feedback loop is active and it performs effects.
165
+ When `query` returns a value, that value is being passed into `effects` lambda to decide which effects should be performed.
166
+ In case new `query` is different from the previous one, new effects are calculated by using `effects` lambda and then performed.
113
167
114
- When query result is `nil`, feedback loops doesn't perform any effect.
168
+ When ` query` returns `nil`, feedback loops doesn't perform any effect.
115
169
116
- - parameter query: State type of the system
117
- - parameter effects: Control state which is subset of state .
170
+ - parameter query: Part of state that controls feedback loop.
171
+ - parameter effects: Chooses which effects to perform for certain query result .
118
172
- returns: Feedback loop performing the effects.
119
173
*/
120
- public func react< State, Control , Event> (
121
- query: @escaping ( State ) -> Control ? ,
122
- effects: @escaping ( Control ) -> Signal < Event >
123
- ) -> ( Driver < State > ) -> Signal < Event > {
174
+ public func react< State, Query , Event> (
175
+ query: @escaping ( State ) -> Query ? ,
176
+ effects: @escaping ( Query ) -> Signal < Event >
177
+ ) -> ( Driver < State > ) -> Signal < Event > {
124
178
return { state in
125
179
let observableSchedulerContext = ObservableSchedulerContext < State > (
126
180
source: state. asObservable ( ) ,
@@ -132,22 +186,22 @@ public func react<State, Control, Event>(
132
186
}
133
187
134
188
/**
135
- Control feedback loop that tries to immediatelly perform the latest required effect.
136
-
137
189
* State: State type of the system.
138
- * Control : Subset of state used to control the feedback loop.
190
+ * Query : Subset of state used to control the feedback loop.
139
191
140
- When query result exists (not `nil`), feedback loop is active and it performs effects.
192
+ When ` query` returns some set of values, each value is being passed into `effects` lambda to decide which effects should be performed .
141
193
142
- When query result is `nil`, feedback loops doesn't perform any effect.
194
+ * Effects are not interrupted for elements in the new `query` that were present in the `old` query.
195
+ * Effects are cancelled for elements present in `old` query but not in `new` query.
196
+ * In case new elements are present in `new` query (and not in `old` query) they are being passed to the `effects` lambda and resulting effects are being performed.
143
197
144
- - parameter query: State type of the system
145
- - parameter effects: Control state which is subset of state .
198
+ - parameter query: Part of state that controls feedback loop.
199
+ - parameter effects: Chooses which effects to perform for certain query element .
146
200
- returns: Feedback loop performing the effects.
147
201
*/
148
- public func react< State, Control , Event> (
149
- query: @escaping ( State ) -> Set < Control > ,
150
- effects: @escaping ( Control ) -> Observable < Event >
202
+ public func react< State, Query , Event> (
203
+ query: @escaping ( State ) -> Set < Query > ,
204
+ effects: @escaping ( Query ) -> Observable < Event >
151
205
) -> ( ObservableSchedulerContext < State > ) -> Observable < Event > {
152
206
return { state in
153
207
let query = state. map ( query)
@@ -169,35 +223,35 @@ public func react<State, Control, Event>(
169
223
extension ObservableType {
170
224
// This is important to avoid reentrancy issues. Completed event is only used for cleanup
171
225
fileprivate func takeUntilWithCompletedAsync< O> ( _ other: Observable < O > , scheduler: ImmediateSchedulerType ) -> Observable < E > {
172
- // this little piggy will delay completed event
173
- let completeAsSoonAsPossible = Observable < E > . empty ( ) . observeOn ( scheduler)
174
- return other
175
- . take ( 1 )
176
- . map { _ in completeAsSoonAsPossible }
177
- // this little piggy will ensure self is being run first
178
- . startWith ( self . asObservable ( ) )
179
- // this little piggy will ensure that new events are being blocked immediatelly
180
- . switchLatest ( )
226
+ // this little piggy will delay completed event
227
+ let completeAsSoonAsPossible = Observable < E > . empty ( ) . observeOn ( scheduler)
228
+ return other
229
+ . take ( 1 )
230
+ . map { _ in completeAsSoonAsPossible }
231
+ // this little piggy will ensure self is being run first
232
+ . startWith ( self . asObservable ( ) )
233
+ // this little piggy will ensure that new events are being blocked immediatelly
234
+ . switchLatest ( )
181
235
}
182
236
}
183
237
184
238
/**
185
- Control feedback loop that tries to immediatelly perform the latest required effect.
186
-
187
239
* State: State type of the system.
188
- * Control : Subset of state used to control the feedback loop.
240
+ * Query : Subset of state used to control the feedback loop.
189
241
190
- When query result exists (not `nil`), feedback loop is active and it performs effects.
242
+ When ` query` returns some set of values, each value is being passed into `effects` lambda to decide which effects should be performed .
191
243
192
- When query result is `nil`, feedback loops doesn't perform any effect.
244
+ * Effects are not interrupted for elements in the new `query` that were present in the `old` query.
245
+ * Effects are cancelled for elements present in `old` query but not in `new` query.
246
+ * In case new elements are present in `new` query (and not in `old` query) they are being passed to the `effects` lambda and resulting effects are being performed.
193
247
194
- - parameter query: State type of the system
195
- - parameter effects: Control state which is subset of state .
248
+ - parameter query: Part of state that controls feedback loop.
249
+ - parameter effects: Chooses which effects to perform for certain query element .
196
250
- returns: Feedback loop performing the effects.
197
251
*/
198
- public func react< State, Control , Event> (
199
- query: @escaping ( State ) -> Set < Control > ,
200
- effects: @escaping ( Control ) -> Signal < Event >
252
+ public func react< State, Query , Event> (
253
+ query: @escaping ( State ) -> Set < Query > ,
254
+ effects: @escaping ( Query ) -> Signal < Event >
201
255
) -> ( Driver < State > ) -> Signal < Event > {
202
256
return { ( state: Driver < State > ) -> Signal < Event > in
203
257
let observableSchedulerContext = ObservableSchedulerContext < State > (
@@ -225,15 +279,15 @@ extension Observable {
225
279
Contains subscriptions and events.
226
280
- `subscriptions` map a system state to UI presentation.
227
281
- `events` map events from UI to events of a given system.
228
- */
282
+ */
229
283
public class Bindings < Event> : Disposable {
230
284
fileprivate let subscriptions : [ Disposable ]
231
285
fileprivate let events : [ Observable < Event > ]
232
286
233
287
/**
234
288
- parameters:
235
- - subscriptions: mappings of a system state to UI presentation.
236
- - events: mappings of events from UI to events of a given system
289
+ - subscriptions: mappings of a system state to UI presentation.
290
+ - events: mappings of events from UI to events of a given system
237
291
*/
238
292
public init ( subscriptions: [ Disposable ] , events: [ Observable < Event > ] ) {
239
293
self . subscriptions = subscriptions
@@ -242,8 +296,8 @@ public class Bindings<Event>: Disposable {
242
296
243
297
/**
244
298
- parameters:
245
- - subscriptions: mappings of a system state to UI presentation.
246
- - events: mappings of events from UI to events of a given system
299
+ - subscriptions: mappings of a system state to UI presentation.
300
+ - events: mappings of events from UI to events of a given system
247
301
*/
248
302
public init ( subscriptions: [ Disposable ] , events: [ Signal < Event > ] ) {
249
303
self . subscriptions = subscriptions
@@ -302,15 +356,16 @@ public func bind<State, Event>(_ bindings: @escaping (Driver<State>) -> (Binding
302
356
*/
303
357
public func bind< State, Event, WeakOwner> ( _ owner: WeakOwner , _ bindings: @escaping ( WeakOwner , Driver < State > ) -> ( Bindings < Event > ) )
304
358
-> ( Driver < State > ) -> Signal < Event > where WeakOwner: AnyObject {
305
- return bind ( bindingsStrongify ( owner, bindings) )
359
+ return bind ( bindingsStrongify ( owner, bindings) )
306
360
}
307
361
308
362
private func bindingsStrongify< Event, O, WeakOwner> ( _ owner: WeakOwner , _ bindings: @escaping ( WeakOwner , O ) -> ( Bindings < Event > ) )
309
363
-> ( O ) -> ( Bindings < Event > ) where WeakOwner: AnyObject {
310
- return { [ weak owner] state -> Bindings < Event > in
311
- guard let strongOwner = owner else {
312
- return Bindings ( subscriptions: [ ] , events: [ Observable < Event > ] ( ) )
364
+ return { [ weak owner] state -> Bindings < Event > in
365
+ guard let strongOwner = owner else {
366
+ return Bindings ( subscriptions: [ ] , events: [ Observable < Event > ] ( ) )
367
+ }
368
+ return bindings ( strongOwner, state)
313
369
}
314
- return bindings ( strongOwner, state)
315
- }
316
370
}
371
+
0 commit comments