Skip to content

Commit 55f2962

Browse files
committed
Merge pull request ReactiveX#71 from kzaher/feature/documentation
Documentation
2 parents 1eccea6 + b3f5364 commit 55f2962

File tree

40 files changed

+2090
-579
lines changed

40 files changed

+2090
-579
lines changed

Documentation/API.md

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
API
2+
===
3+
14
## RxSwift supported operators
25

36
In some cases there are multiple aliases for the same operator, because on different platforms / implementations, the same operation is sometimes called differently. Sometimes this is because historical reasons, sometimes because of reserved language keywords.
@@ -73,7 +76,7 @@ Operators are stateless by default.
7376
* [`replay`](http://reactivex.io/documentation/operators/replay.html)
7477
* variable / sharedWithCachedLastResult
7578

76-
Creating new operators is also pretty straightforward.
79+
Creating new operators is also pretty straightforward.
7780

7881
## RxCocoa extensions
7982

@@ -99,17 +102,18 @@ extension NSObject {
99102
```swift
100103
extension NSObject {
101104

102-
public func rx_observe<Element>(keyPath: String) -> Observable<Element?> {}
103-
104-
public func rx_observe<Element>(keyPath: String, options: NSKeyValueObservingOptions) -> Observable<Element?> {}
105-
106-
public func rx_observe<Element>(keyPath: String, options: NSKeyValueObservingOptions, retainSelf: Bool) -> Observable<Element?> {}
105+
public func rx_observe<Element>(
106+
keyPath: String,
107+
options: NSKeyValueObservingOptions = .New | .Initial,
108+
retainSelf: Bool = true
109+
) -> Observable<Element?> {}
107110

108111
#if !DISABLE_SWIZZLING
109112

110-
public func rx_observeWeakly<Element>(keyPath: String) -> Observable<Element?> {}
111-
112-
public func rx_observeWeakly<Element>(keyPath: String, options: NSKeyValueObservingOptions) -> Observable<Element?> {}
113+
public func rx_observeWeakly<Element>(
114+
keyPath: String,
115+
options: NSKeyValueObservingOptions = .New | .Initial
116+
) -> Observable<Element?> {}
113117

114118
#endif
115119
}
@@ -139,7 +143,7 @@ extension NSNotificationCenter {
139143

140144
```swift
141145
class DelegateProxy {
142-
146+
143147
public func observe(selector: Selector) -> Observable<[AnyObject]> {}
144148

145149
}
@@ -215,11 +219,11 @@ extension UITextField {
215219

216220
```swift
217221
extension UITextView {
218-
222+
219223
override func rx_createDelegateProxy() -> RxScrollViewDelegateProxy { }
220-
224+
221225
public var rx_text: Observable<String> { }
222-
226+
223227
}
224228
```
225229

@@ -256,7 +260,7 @@ extension UIImageView {
256260

257261
public func rx_subscribeImageTo
258262
(animated: Bool)
259-
(source: Observable<UIImage?>)
263+
(source: Observable<UIImage?>)
260264
-> Disposable {}
261265

262266
}
@@ -268,7 +272,7 @@ extension UIScrollView {
268272
public var rx_delegate: DelegateProxy {}
269273

270274
public func rx_setDelegate(delegate: UIScrollViewDelegate) {}
271-
275+
272276
public var rx_contentOffset: Observable<CGPoint> {}
273277

274278
}
@@ -292,7 +296,7 @@ extension UISlider {
292296

293297
```swift
294298
extension UITableView {
295-
299+
296300
public var rx_dataSource: DelegateProxy {}
297301

298302
public func rx_setDataSource(dataSource: UITableViewDataSource) -> Disposable {}
@@ -322,11 +326,11 @@ extension UITableView {
322326

323327
```swift
324328
extension UICollectionView {
325-
329+
326330
public var rx_dataSource: DelegateProxy {}
327331

328332
public func rx_setDataSource(dataSource: UICollectionViewDataSource) -> Disposable {}
329-
333+
330334
public func rx_subscribeWithReactiveDataSource<DataSource: protocol<RxCollectionViewDataSourceType, UICollectionViewDataSource>>(dataSource: DataSource)
331335
-> Observable<DataSource.Element> -> Disposable {}
332336

@@ -374,7 +378,7 @@ extension UIAlertView {
374378
public var rx_clickedButtonAtIndex: Observable<Int> {}
375379

376380
public var rx_willDismissWithButtonIndex: Observable<Int> {}
377-
381+
378382
public var rx_didDismissWithButtonIndex: Observable<Int> {}
379383

380384
}
@@ -427,7 +431,7 @@ extension NSButton {
427431
extension NSImageView {
428432

429433
public func rx_subscribeImageTo(source: Observable<NSImage?>) -> Disposable {}
430-
434+
431435
public func rx_subscribeImageTo
432436
(animated: Bool)
433437
(source: Observable<NSImage?>) -> Disposable {}
@@ -438,10 +442,9 @@ extension NSImageView {
438442
extension NSTextField {
439443

440444
public var rx_delegate: DelegateProxy {}
441-
445+
442446
public var rx_text: Observable<String> {}
443447

444448
public func rx_subscribeTextTo(source: Observable<String>) -> Disposable {}
445-
}
449+
}
446450
```
447-

Documentation/DesignRationale.md

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
Design Rationale
2+
================
3+
4+
## Why error type isn't generic
5+
6+
```Swift
7+
enum Event<Element> {
8+
case Next(Element) // next element of a sequence
9+
case Error(ErrorType) // sequence failed with error
10+
case Completed // sequence terminated successfully
11+
}
12+
```
13+
14+
Let's discuss pros and cons of `ErrorType` being generic.
15+
16+
If you have generic error type you create additional impedance mismatch between two observables.
17+
18+
Let's say you have:
19+
20+
`Observable<String, E1>` and `Observable<String, E2>`
21+
22+
There isn't much you can do with them without figuring out what will be the resulting error type.
23+
24+
Will it be `E1`, `E2` or some new `E3` maybe? So you need a new set of operators just to solve that impedance mismatch.
25+
26+
This for sure hurts composition properties, and Rx really doesn't care about why sequence fails, it just forwards failure further.
27+
28+
There is additional problem that maybe in some cases operators will fail for some internal error, and in that case you won't be able to construct resulting error and report failure.
29+
30+
But ok, let's ignore that and assume we can use that to model sequences that don't error out. It looks like it could be useful for that purpose?
31+
32+
Well yes, it potentially could be, but lets consider why would you want to use sequences that don't error out.
33+
34+
One obvious application would be for permanent streams in UI layer that drive entire UI. But when you consider that case, it's not really only sufficient to use compiler to prove that sequences don't error out, you also need to prove other properties. Like that elements are observed on `MainScheduler`.
35+
36+
What you really need is a generic way to prove traits for sequences (`Observables`). And you could be interested in a lot of properties. For example:
37+
38+
* sequence terminates in finite time (server side)
39+
* sequence contains only one element (if you are running some computation)
40+
* sequence doesn't error out, never terminates and elements are delivered on main scheduler (UI)
41+
* sequence doesn't error out, never terminates and elements are delivered on main scheduler, and has refcounted sharing (UI)
42+
* sequence doesn't error out, never terminates and elements are delivered on specific background scheduler (audio engine)
43+
44+
What you really want is a general compiler enforced system of traits for observable sequences, and a set of invariant operators for those wanted properties.
45+
46+
A good analogy IMHO would be
47+
48+
```
49+
1, 3.14, e, 2.79, 1 + 1i <-> Observable<E>
50+
1m/s, 1T, 5kg, 1.3 pounds <-> Errorless observable, UI observable, Finite observable ...
51+
```
52+
53+
There are many ways how to do that in Swift by either using composition or inheritance of observables.
54+
55+
Additional benefit of using unit system is that you can prove that UI code is executing on same scheduler and thus use lockless operators for all transformations.
56+
57+
Since Rx already doesn't have locks for single sequence operations, and all of the remaining locks are in statefull components (aka UI), that would practically remove all of the remaining locks out of Rx code and create compiler enforced lockless Rx code.
58+
59+
So IMHO, there really is no benefit of using typed Errors that couldn't be achieved cleaner in other ways while preserving Rx compositional semantics. And other ways also have huge other benefits.
60+
61+
## Pipe operator
62+
63+
This is the definition of `>-` operator.
64+
65+
```swift
66+
func >- <In, Out>(lhs: In, rhs: In -> Out) -> Out {
67+
return rhs(lhs)
68+
}
69+
```
70+
71+
This enables us to write
72+
73+
```swift
74+
a >- map { $0 * 2 } >- filter { $0 > 0 }
75+
```
76+
77+
instead of
78+
79+
```swift
80+
a.map { $0 * 2 }.filter { $0 > 0 }
81+
```
82+
83+
This is another explanation:
84+
85+
```swift
86+
a >- b >- c is equivalent to c(b(a))
87+
```
88+
89+
So why was this introduced and not just use "." and extensions? Short answer is that Swift extensions weren't powerful enough, but there are other reasons as well.
90+
91+
Next version of RxSwift for Swift 2.0 will probably also include extensions that will enable the use of
92+
`.`.
93+
94+
">-" also enables us to chain results easily. For example, if using protocol extensions typical example would look like this.
95+
96+
```swift
97+
disposeBag.addDisposable(
98+
observable
99+
.map { n in
100+
n * 2
101+
}
102+
.subscribeNext { n in
103+
print(n)
104+
}
105+
)
106+
```
107+
108+
This code could be written more elegantly using `>-` operator.
109+
110+
```swift
111+
observable
112+
>- map { n in
113+
n * 2
114+
}
115+
>- subscribeNext { n in
116+
print(n)
117+
}
118+
>- disposeBag.addDisposable
119+
```
120+
121+
None of the Rx public interfaces depend on the >- operator.
122+
123+
It was actually introduced quite late and you can use Rx operators (map, filter ...) without it.
124+
125+
### Replacing `>-` with your own operator
126+
127+
If you dislike `>-` operator and want to use `|>` or `~>` operators, just define them in your project in this form:
128+
129+
```swift
130+
infix operator |> { associativity left precedence 91 }
131+
132+
public func |> <In, Out>(source: In, @noescape transform: In -> Out) -> Out {
133+
return transform(source)
134+
}
135+
```
136+
137+
or
138+
139+
```swift
140+
infix operator ~> { associativity left precedence 91 }
141+
142+
public func ~> <In, Out>(source: In, @noescape transform: In -> Out) -> Out {
143+
return transform(source)
144+
}
145+
```
146+
147+
and you can use them instead of `>-` operator.
148+
149+
```swift
150+
let a /*: Observable<Int>*/ = Variable(1)
151+
let b /*: Observable<Int>*/ = Variable(2)
152+
153+
combineLatest(a, b) { $0 + $1 }
154+
|> filter { $0 >= 0 }
155+
|> map { "\($0) is positive" }
156+
|> subscribeNext { println($0) }
157+
```
158+
159+
```swift
160+
let a /*: Observable<Int>*/ = Variable(1)
161+
let b /*: Observable<Int>*/ = Variable(2)
162+
163+
combineLatest(a, b) { $0 + $1 }
164+
~> filter { $0 >= 0 }
165+
~> map { "\($0) is positive" }
166+
~> subscribeNext { println($0) }
167+
```
168+
169+
### Why wasn't standard function application operator used?
170+
171+
I've first tried to find a similar operator in swift core libraries, but couldn't find it. That meant that I'll need to define something myself or find some third party library that contains reference function application operator definition and use it.
172+
Otherwise all of the example code would be unreadable.
173+
174+
### Why wasn't some standard library used for that operator?
175+
176+
Well, I'm not sure there is a clear consensus in the community about funtion application operators or libraries that define them.
177+
178+
### Why wasn't function application operator defined only for `Observables` and `Disposables`?
179+
180+
One of the solutions could have been to provide a specialized operator that just works for `Observables` and `Disposables`.
181+
In that case, if an identically named general purpose function application operator is defined somewhere else, there would still be collision, priority or ambiguity problems.
182+
183+
### Why wasn't some more standard operator like `|>` or `~>` used?
184+
185+
`|>` or `~>` are probably more commonly used operators in swift, so if there was another definition for them in Rx as general purpose function application operators, there is a high probability they would collide with definitions in other frameworks or project.
186+
187+
The simplest and safest solution IMHO was to create some new operator that made sense in this context and there is a low probability anyone else uses it.
188+
In case the operator naming choice was wrong, name is rare and community eventually reaches consensus on the matter, it's more easier to find and replace it in user projects.
189+
190+
### Rationale why `>-` was chosen
191+
192+
* It's short, only two characters
193+
* It looks like a sink to the right, which is a function it actually performs, so it's intuitive.
194+
* It doesn't create a lot of visual noise. `|>` compared to `>-` IMHO looks a lot more intrusive. When my visual cortex parses `|>` it creates an illusion of a filled triangle, and when it parses `>-`, it sees three lines that don't cover any surface area, but are easily recognizable. Of course, that experience can be different for other people, but since I really wanted to create something that's pleasurable for me to use, that's a good argument. I'm just hoping that other people have the same experience.
195+
* In the worst case scenario, if this operator is awkward to somebody, they can easily replace it using instructions above.

0 commit comments

Comments
 (0)