Skip to content

Commit a0d94e1

Browse files
authored
Merge pull request #101 from iv-mexx/feature/propertyDocumentation
Feature/property documentation
2 parents b3361c2 + 146aaa9 commit a0d94e1

File tree

3 files changed

+280
-9
lines changed

3 files changed

+280
-9
lines changed

Documentation/FrameworkOverview.md

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,15 @@ For interaction with `NSControl` or `UIControl`, RAC provides the
136136

137137
## Properties
138138

139-
A **property**, represented by the [`PropertyType`][Property] protocol,
139+
A **property**, represented by the [`PropertyProtocol`][Property],
140140
stores a value and notifies observers about future changes to that value.
141141

142142
The current value of a property can be obtained from the `value` getter. The
143143
`producer` getter returns a [signal producer](#signal-producers) that will send
144-
the property’s current value, followed by all changes over time.
144+
the property’s current value, followed by all changes over time. The `signal` getter returns a [signal](#signals) that will send all changes over time, but not the initial value.
145145

146146
The `<~` operator can be used to bind properties in different ways. Note that in
147-
all cases, the target has to be a [`MutablePropertyType`][Property].
147+
all cases, the target has to be a binding target, represented by the [`BindingTargetProtocol`][BindingTarget]. All mutable property types, represented by the [`MutablePropertyProtocol`][MutableProperty], are inherently binding targets.
148148

149149
* `property <~ signal` binds a [signal](#signals) to the property, updating the
150150
property’s value to the latest value sent by the signal.
@@ -153,12 +153,7 @@ all cases, the target has to be a [`MutablePropertyType`][Property].
153153
* `property <~ otherProperty` binds one property to another, so that the destination
154154
property’s value is updated whenever the source property is updated.
155155

156-
The [`DynamicProperty`][Property] type can be used to bridge to Objective-C APIs
157-
that require Key-Value Coding (KVC) or Key-Value Observing (KVO), like
158-
`NSOperation`. Note that most AppKit and UIKit properties do _not_ support KVO,
159-
so their changes should be observed through other mechanisms.
160-
[`MutableProperty`][Property] should be preferred over dynamic properties
161-
whenever possible!
156+
Properties provide a number of transformations like `map`, `combineLatest` or `zip` for manipulation similar to [signal](#signals) and [signal producer](#signal-producers)
162157

163158
## Disposables
164159

@@ -199,6 +194,7 @@ do not allow tasks to be reordered or depend on one another.
199194
[Design Guidelines]: DesignGuidelines.md
200195
[BasicOperators]: BasicOperators.md
201196
[README]: ../README.md
197+
[ReactiveCocoa]: https://github.com/ReactiveCocoa/
202198
[Signal]: ../Sources/Signal.swift
203199
[SignalProducer]: ../Sources/SignalProducer.swift
204200
[Action]: ../Sources/Action.swift
@@ -208,3 +204,4 @@ do not allow tasks to be reordered or depend on one another.
208204
[Property]: ../Sources/Property.swift
209205
[Event]: ../Sources/Event.swift
210206
[Observer]: ../Sources/Observer.swift
207+
[BindingTarget]: ../Sources/UnidirectionalBinding.swift
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
/*:
2+
> # IMPORTANT: To use `ReactiveSwift.playground`, please:
3+
4+
1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveSwift project root directory:
5+
- `script/bootstrap`
6+
**OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed
7+
- `carthage checkout`
8+
1. Open `ReactiveSwift.xcworkspace`
9+
1. Build `Result-Mac` scheme
10+
1. Build `ReactiveSwift-macOS` scheme
11+
1. Finally open the `ReactiveSwift.playground`
12+
1. Choose `View > Show Debug Area`
13+
*/
14+
import Result
15+
import ReactiveSwift
16+
import Foundation
17+
/*:
18+
## Property
19+
20+
A **property**, represented by the [`PropertyProtocol`](https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Sources/Property.swift) ,
21+
stores a value and notifies observers about future changes to that value.
22+
23+
- The current value of a property can be obtained from the `value` getter.
24+
- The `producer` getter returns a [signal producer](SignalProductr) that will send the property’s current value, followed by all changes over time.
25+
- The `signal` getter returns a [signal](Signal) that will send all changes over time, but not the initial value.
26+
27+
*/
28+
scopedExample("Creation") {
29+
let mutableProperty = MutableProperty(1)
30+
31+
// The value of the property can be accessed via its `value` attribute
32+
print("Property has initial value \(mutableProperty.value)")
33+
// The properties value can be observed via its `producer` or `signal attribute`
34+
// Note, how the `producer` immediately sends the initial value, but the `signal` only sends new values
35+
mutableProperty.producer.startWithValues {
36+
print("mutableProperty.producer receied \($0)")
37+
}
38+
mutableProperty.signal.observeValues {
39+
print("mutableProperty.signal received \($0)")
40+
}
41+
42+
print("---")
43+
print("Setting new value for mutableProperty: 2")
44+
mutableProperty.value = 2
45+
46+
print("---")
47+
// If a property should be exposed for readonly access, it can be wrapped in a Property
48+
let property = Property(mutableProperty)
49+
50+
print("Reading value of readonly property: \(property.value)")
51+
property.signal.observeValues {
52+
print("property.signal received \($0)")
53+
}
54+
55+
// Its not possible to set the value of a Property
56+
// readonlyProperty.value = 3
57+
// But you can still change the value of the mutableProperty and observe its change on the property
58+
print("---")
59+
print("Setting new value for mutableProperty: 3")
60+
mutableProperty.value = 3
61+
62+
// Constant properties can be created by using the `Property(value:)` initializer
63+
let constant = Property(value: 1)
64+
// constant.value = 2 // The value of a constant property can not be changed
65+
}
66+
/*:
67+
### Binding
68+
69+
The `<~` operator can be used to bind properties in different ways. Note that in
70+
all cases, the target has to be a binding target, represented by the [`BindingTargetProtocol`](https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Sources/UnidirectionalBinding.swift). All mutable property types, represented by the [`MutablePropertyProtocol`](https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Sources/Property.swift#L28), are inherently binding targets.
71+
72+
* `property <~ signal` binds a [signal](#signals) to the property, updating the
73+
property’s value to the latest value sent by the signal.
74+
* `property <~ producer` starts the given [signal producer](#signal-producers),
75+
and binds the property’s value to the latest value sent on the started signal.
76+
* `property <~ otherProperty` binds one property to another, so that the destination
77+
property’s value is updated whenever the source property is updated.
78+
*/
79+
scopedExample("Binding from SignalProducer") {
80+
let producer = SignalProducer<Int, NoError> { observer, _ in
81+
print("New subscription, starting operation")
82+
observer.send(value: 1)
83+
observer.send(value: 2)
84+
}
85+
let property = MutableProperty(0)
86+
property.producer.startWithValues {
87+
print("Property received \($0)")
88+
}
89+
90+
// Notice how the producer will start the work as soon it is bound to the property
91+
property <~ producer
92+
}
93+
94+
scopedExample("Binding from Signal") {
95+
let (signal, observer) = Signal<Int, NoError>.pipe()
96+
let property = MutableProperty(0)
97+
property.producer.startWithValues {
98+
print("Property received \($0)")
99+
}
100+
101+
property <~ signal
102+
103+
print("Sending new value on signal: 1")
104+
observer.send(value: 1)
105+
106+
print("Sending new value on signal: 2")
107+
observer.send(value: 2)
108+
}
109+
110+
scopedExample("Binding from other Property") {
111+
let property = MutableProperty(0)
112+
property.producer.startWithValues {
113+
print("Property received \($0)")
114+
}
115+
116+
let otherProperty = MutableProperty(0)
117+
118+
// Notice how property receives another value of 0 as soon as the binding is established
119+
property <~ otherProperty
120+
121+
print("Setting new value for otherProperty: 1")
122+
otherProperty.value = 1
123+
124+
print("Setting new value for otherProperty: 2")
125+
otherProperty.value = 2
126+
}
127+
/*:
128+
### Transformations
129+
130+
Properties provide a number of transformations like `map`, `combineLatest` or `zip` for manipulation similar to [signal](Signal) and [signal producer](SignalProducer)
131+
*/
132+
scopedExample("`map`") {
133+
let property = MutableProperty(0)
134+
let mapped = property.map { $0 * 2 }
135+
mapped.producer.startWithValues {
136+
print("Mapped property received \($0)")
137+
}
138+
139+
print("Setting new value for property: 1")
140+
property.value = 1
141+
142+
print("Setting new value for property: 2")
143+
property.value = 2
144+
}
145+
146+
scopedExample("`skipRepeats`") {
147+
let property = MutableProperty(0)
148+
let skipRepeatsProperty = property.skipRepeats()
149+
150+
property.producer.startWithValues {
151+
print("Property received \($0)")
152+
}
153+
skipRepeatsProperty.producer.startWithValues {
154+
print("Skip-Repeats property received \($0)")
155+
}
156+
157+
print("Setting new value for property: 0")
158+
property.value = 0
159+
print("Setting new value for property: 1")
160+
property.value = 1
161+
print("Setting new value for property: 1")
162+
property.value = 1
163+
print("Setting new value for property: 0")
164+
property.value = 0
165+
}
166+
167+
scopedExample("`uniqueValues`") {
168+
let property = MutableProperty(0)
169+
let unique = property.uniqueValues()
170+
property.producer.startWithValues {
171+
print("Property received \($0)")
172+
}
173+
unique.producer.startWithValues {
174+
print("Unique values property received \($0)")
175+
}
176+
177+
print("Setting new value for property: 0")
178+
property.value = 0
179+
print("Setting new value for property: 1")
180+
property.value = 1
181+
print("Setting new value for property: 1")
182+
property.value = 1
183+
print("Setting new value for property: 0")
184+
property.value = 0
185+
186+
}
187+
188+
scopedExample("`combineLatest`") {
189+
let propertyA = MutableProperty(0)
190+
let propertyB = MutableProperty("A")
191+
let combined = propertyA.combineLatest(with: propertyB)
192+
combined.producer.startWithValues {
193+
print("Combined property received \($0)")
194+
}
195+
196+
print("Setting new value for propertyA: 1")
197+
propertyA.value = 1
198+
199+
print("Setting new value for propertyB: 'B'")
200+
propertyB.value = "B"
201+
202+
print("Setting new value for propertyB: 'C'")
203+
propertyB.value = "C"
204+
205+
print("Setting new value for propertyB: 'D'")
206+
propertyB.value = "D"
207+
208+
print("Setting new value for propertyA: 2")
209+
propertyA.value = 2
210+
}
211+
212+
scopedExample("`zip`") {
213+
let propertyA = MutableProperty(0)
214+
let propertyB = MutableProperty("A")
215+
let zipped = propertyA.zip(with: propertyB)
216+
zipped.producer.startWithValues {
217+
print("Zipped property received \($0)")
218+
}
219+
220+
print("Setting new value for propertyA: 1")
221+
propertyA.value = 1
222+
223+
print("Setting new value for propertyB: 'B'")
224+
propertyB.value = "B"
225+
226+
// Observe that, in contrast to `combineLatest`, setting a new value for propertyB does not cause a new value for the zipped property until propertyA has a new value as well
227+
print("Setting new value for propertyB: 'C'")
228+
propertyB.value = "C"
229+
230+
print("Setting new value for propertyB: 'D'")
231+
propertyB.value = "D"
232+
233+
print("Setting new value for propertyA: 2")
234+
propertyA.value = 2
235+
}
236+
237+
scopedExample("`flatten`") {
238+
let property1 = MutableProperty("0")
239+
let property2 = MutableProperty("A")
240+
let property3 = MutableProperty("!")
241+
let property = MutableProperty(property1)
242+
// Try different merge strategies and see how the results change
243+
property.flatten(.latest).producer.startWithValues {
244+
print("Flattened property receive \($0)")
245+
}
246+
247+
print("Sending new value on property1: 1")
248+
property1.value = "1"
249+
250+
print("Sending new value on property: property2")
251+
property.value = property2
252+
253+
print("Sending new value on property1: 2")
254+
property1.value = "2"
255+
256+
print("Sending new value on property2: B")
257+
property2.value = "B"
258+
259+
print("Sending new value on property1: 3")
260+
property1.value = "3"
261+
262+
print("Sending new value on property: property3")
263+
property.value = property3
264+
265+
print("Sending new value on property3: ?")
266+
property3.value = "?"
267+
268+
print("Sending new value on property2: C")
269+
property2.value = "C"
270+
271+
print("Sending new value on property1: 4")
272+
property1.value = "4"
273+
}

ReactiveSwift.playground/contents.xcplayground

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
<page name='Sandbox'/>
55
<page name='SignalProducer'/>
66
<page name='Signal'/>
7+
<page name='Property'/>
78
</pages>
89
</playground>

0 commit comments

Comments
 (0)