|
| 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 | +} |
0 commit comments