Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions Documentation/FrameworkOverview.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,15 @@ For interaction with `NSControl` or `UIControl`, RAC provides the

## Properties

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

The current value of a property can be obtained from the `value` getter. The
`producer` getter returns a [signal producer](#signal-producers) that will send
the property’s current value, followed by all changes over time.
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.

The `<~` operator can be used to bind properties in different ways. Note that in
all cases, the target has to be a [`MutablePropertyType`][Property].
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.

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

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

## Disposables

Expand Down Expand Up @@ -199,6 +194,7 @@ do not allow tasks to be reordered or depend on one another.
[Design Guidelines]: DesignGuidelines.md
[BasicOperators]: BasicOperators.md
[README]: ../README.md
[ReactiveCocoa]: https://github.com/ReactiveCocoa/
[Signal]: ../Sources/Signal.swift
[SignalProducer]: ../Sources/SignalProducer.swift
[Action]: ../Sources/Action.swift
Expand All @@ -208,3 +204,4 @@ do not allow tasks to be reordered or depend on one another.
[Property]: ../Sources/Property.swift
[Event]: ../Sources/Event.swift
[Observer]: ../Sources/Observer.swift
[BindingTarget]: ../Sources/UnidirectionalBinding.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
/*:
> # IMPORTANT: To use `ReactiveSwift.playground`, please:

1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveSwift project root directory:
- `script/bootstrap`
**OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed
- `carthage checkout`
1. Open `ReactiveSwift.xcworkspace`
1. Build `Result-Mac` scheme
1. Build `ReactiveSwift-macOS` scheme
1. Finally open the `ReactiveSwift.playground`
1. Choose `View > Show Debug Area`
*/
import Result
import ReactiveSwift
import Foundation
/*:
## Property

A **property**, represented by the [`PropertyProtocol`](https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Sources/Property.swift) ,
stores a value and notifies observers about future changes to that value.

- The current value of a property can be obtained from the `value` getter.
- The `producer` getter returns a [signal producer](SignalProductr) that will send the property’s current value, followed by all changes over time.
- The `signal` getter returns a [signal](Signal) that will send all changes over time, but not the initial value.

*/
scopedExample("Creation") {
let mutableProperty = MutableProperty(1)

// The value of the property can be accessed via its `value` attribute
print("Property has initial value \(mutableProperty.value)")
// The properties value can be observed via its `producer` or `signal attribute`
// Note, how the `producer` immediately sends the initial value, but the `signal` only sends new values
mutableProperty.producer.startWithValues {
print("mutableProperty.producer receied \($0)")
}
mutableProperty.signal.observeValues {
print("mutableProperty.signal received \($0)")
}

print("---")
print("Setting new value for mutableProperty: 2")
mutableProperty.value = 2

print("---")
// If a property should be exposed for readonly access, it can be wrapped in a Property
let property = Property(mutableProperty)

print("Reading value of readonly property: \(property.value)")
property.signal.observeValues {
print("property.signal received \($0)")
}

// Its not possible to set the value of a Property
// readonlyProperty.value = 3
// But you can still change the value of the mutableProperty and observe its change on the property
print("---")
print("Setting new value for mutableProperty: 3")
mutableProperty.value = 3

// Constant properties can be created by using the `Property(value:)` initializer
let constant = Property(value: 1)
// constant.value = 2 // The value of a constant property can not be changed
}
/*:
### Binding

The `<~` operator can be used to bind properties in different ways. Note that in
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.

* `property <~ signal` binds a [signal](#signals) to the property, updating the
property’s value to the latest value sent by the signal.
* `property <~ producer` starts the given [signal producer](#signal-producers),
and binds the property’s value to the latest value sent on the started signal.
* `property <~ otherProperty` binds one property to another, so that the destination
property’s value is updated whenever the source property is updated.
*/
scopedExample("Binding from SignalProducer") {
let producer = SignalProducer<Int, NoError> { observer, _ in
print("New subscription, starting operation")
observer.send(value: 1)
observer.send(value: 2)
}
let property = MutableProperty(0)
property.producer.startWithValues {
print("Property received \($0)")
}

// Notice how the producer will start the work as soon it is bound to the property
property <~ producer
}

scopedExample("Binding from Signal") {
let (signal, observer) = Signal<Int, NoError>.pipe()
let property = MutableProperty(0)
property.producer.startWithValues {
print("Property received \($0)")
}

property <~ signal

print("Sending new value on signal: 1")
observer.send(value: 1)

print("Sending new value on signal: 2")
observer.send(value: 2)
}

scopedExample("Binding from other Property") {
let property = MutableProperty(0)
property.producer.startWithValues {
print("Property received \($0)")
}

let otherProperty = MutableProperty(0)

// Notice how property receives another value of 0 as soon as the binding is established
property <~ otherProperty

print("Setting new value for otherProperty: 1")
otherProperty.value = 1

print("Setting new value for otherProperty: 2")
otherProperty.value = 2
}
/*:
### Transformations

Properties provide a number of transformations like `map`, `combineLatest` or `zip` for manipulation similar to [signal](Signal) and [signal producer](SignalProducer)
*/
scopedExample("`map`") {
let property = MutableProperty(0)
let mapped = property.map { $0 * 2 }
mapped.producer.startWithValues {
print("Mapped property received \($0)")
}

print("Setting new value for property: 1")
property.value = 1

print("Setting new value for property: 2")
property.value = 2
}

scopedExample("`skipRepeats`") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to be a duplication of the more-complete example below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, I created the more complex example for uniqueValues first and then decided to use the same sequence for skipRepeats as well to better illustrate the difference.
I'll clean up the simple example.

let property = MutableProperty(0)
let skipRepeatsProperty = property.skipRepeats()

property.producer.startWithValues {
print("Property received \($0)")
}
skipRepeatsProperty.producer.startWithValues {
print("Skip-Repeats property received \($0)")
}

print("Setting new value for property: 0")
property.value = 0
print("Setting new value for property: 1")
property.value = 1
print("Setting new value for property: 1")
property.value = 1
print("Setting new value for property: 0")
property.value = 0
}

scopedExample("`uniqueValues`") {
let property = MutableProperty(0)
let unique = property.uniqueValues()
property.producer.startWithValues {
print("Property received \($0)")
}
unique.producer.startWithValues {
print("Unique values property received \($0)")
}

print("Setting new value for property: 0")
property.value = 0
print("Setting new value for property: 1")
property.value = 1
print("Setting new value for property: 1")
property.value = 1
print("Setting new value for property: 0")
property.value = 0

}

scopedExample("`combineLatest`") {
let propertyA = MutableProperty(0)
let propertyB = MutableProperty("A")
let combined = propertyA.combineLatest(with: propertyB)
combined.producer.startWithValues {
print("Combined property received \($0)")
}

print("Setting new value for propertyA: 1")
propertyA.value = 1

print("Setting new value for propertyB: 'B'")
propertyB.value = "B"

print("Setting new value for propertyB: 'C'")
propertyB.value = "C"

print("Setting new value for propertyB: 'D'")
propertyB.value = "D"

print("Setting new value for propertyA: 2")
propertyA.value = 2
}

scopedExample("`zip`") {
let propertyA = MutableProperty(0)
let propertyB = MutableProperty("A")
let zipped = propertyA.zip(with: propertyB)
zipped.producer.startWithValues {
print("Zipped property received \($0)")
}

print("Setting new value for propertyA: 1")
propertyA.value = 1

print("Setting new value for propertyB: 'B'")
propertyB.value = "B"

// 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
print("Setting new value for propertyB: 'C'")
propertyB.value = "C"

print("Setting new value for propertyB: 'D'")
propertyB.value = "D"

print("Setting new value for propertyA: 2")
propertyA.value = 2
}

scopedExample("`flatten`") {
let property1 = MutableProperty("0")
let property2 = MutableProperty("A")
let property3 = MutableProperty("!")
let property = MutableProperty(property1)
// Try different merge strategies and see how the results change
property.flatten(.latest).producer.startWithValues {
print("Flattened property receive \($0)")
}

print("Sending new value on property1: 1")
property1.value = "1"

print("Sending new value on property: property2")
property.value = property2

print("Sending new value on property1: 2")
property1.value = "2"

print("Sending new value on property2: B")
property2.value = "B"

print("Sending new value on property1: 3")
property1.value = "3"

print("Sending new value on property: property3")
property.value = property3

print("Sending new value on property3: ?")
property3.value = "?"

print("Sending new value on property2: C")
property2.value = "C"

print("Sending new value on property1: 4")
property1.value = "4"
}
1 change: 1 addition & 0 deletions ReactiveSwift.playground/contents.xcplayground
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
<page name='Sandbox'/>
<page name='SignalProducer'/>
<page name='Signal'/>
<page name='Property'/>
</pages>
</playground>