Deprecates `Variable` implementing `ObservableType` in favor of `asObservable()`, and changes behavior so it completes the sequence once it is deallocated.

This commit is contained in:
Krunoslav Zaher 2015-12-29 23:49:30 +01:00
parent 839051fe9b
commit 8b2984e4e2
11 changed files with 80 additions and 48 deletions

View File

@ -700,7 +700,7 @@ This isn't something that should be practiced often, and is a bad code smell, bu
let kittens = Variable(firstKitten) // again back in Rx monad
kittens
kittens.asObservable()
.map { kitten in
return kitten.purr()
}
@ -873,12 +873,14 @@ Variable wraps a [`Subject`](http://reactivex.io/documentation/subject.html). Mo
It will also broadcast it's current value immediately on subscription.
After variable is deallocated, it will complete the observable sequence returned from `.asObservable()`.
```swift
let variable = Variable(0)
print("Before first subscription ---")
variable
variable.asObservable()
.subscribeNext { n in
print("First \(n)")
}

View File

@ -255,9 +255,10 @@ example("switchLatest") {
let var2 = Variable(200)
// var3 is like an Observable<Observable<Int>>
let var3 = Variable(var1)
let var3 = Variable(var1.asObservable())
let d = var3
.asObservable()
.switchLatest()
.subscribe {
print($0)
@ -268,7 +269,7 @@ example("switchLatest") {
var1.value = 3
var1.value = 4
var3.value = var2
var3.value = var2.asObservable()
var2.value = 201

View File

@ -93,10 +93,10 @@ example("BehaviorSubject") {
example("Variable") {
let disposeBag = DisposeBag()
let variable = Variable("z")
writeSequenceToConsole("1", sequence: variable).addDisposableTo(disposeBag)
writeSequenceToConsole("1", sequence: variable.asObservable()).addDisposableTo(disposeBag)
variable.value = "a"
variable.value = "b"
writeSequenceToConsole("2", sequence: variable).addDisposableTo(disposeBag)
writeSequenceToConsole("2", sequence: variable.asObservable()).addDisposableTo(disposeBag)
variable.value = "c"
variable.value = "d"
}

View File

@ -19,7 +19,7 @@ extension Variable {
*/
@warn_unused_result(message="http://git.io/rxs.uo")
public func asDriver() -> Driver<E> {
let source = self
let source = self.asObservable()
.observeOn(driverObserveOnScheduler)
return Driver(source)
}

View File

@ -45,39 +45,6 @@ class PartialUpdatesViewController : ViewController {
var sections = Variable([NumberSection]())
func skinTableViewDataSource(dataSource: RxTableViewSectionedDataSource<NumberSection>) {
dataSource.cellFactory = { (tv, ip, i) in
let cell = tv.dequeueReusableCellWithIdentifier("Cell")
?? UITableViewCell(style:.Default, reuseIdentifier: "Cell")
cell.textLabel!.text = "\(i)"
return cell
}
dataSource.titleForHeaderInSection = { [unowned dataSource] (section: Int) -> String in
return dataSource.sectionAtIndex(section).model
}
}
func skinCollectionViewDataSource(dataSource: RxCollectionViewSectionedDataSource<NumberSection>) {
dataSource.cellFactory = { (cv, ip, i) in
let cell = cv.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: ip) as! NumberCell
cell.value!.text = "\(i)"
return cell
}
dataSource.supplementaryViewFactory = { [unowned dataSource] (cv, kind, ip) in
let section = cv.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "Section", forIndexPath: ip) as! NumberSectionView
section.value!.text = "\(dataSource.sectionAtIndex(ip.section).model)"
return section
}
}
override func viewDidLoad() {
super.viewDidLoad()
@ -162,6 +129,39 @@ class PartialUpdatesViewController : ViewController {
.addDisposableTo(disposeBag)
}
func skinTableViewDataSource(dataSource: RxTableViewSectionedDataSource<NumberSection>) {
dataSource.cellFactory = { (tv, ip, i) in
let cell = tv.dequeueReusableCellWithIdentifier("Cell")
?? UITableViewCell(style:.Default, reuseIdentifier: "Cell")
cell.textLabel!.text = "\(i)"
return cell
}
dataSource.titleForHeaderInSection = { [unowned dataSource] (section: Int) -> String in
return dataSource.sectionAtIndex(section).model
}
}
func skinCollectionViewDataSource(dataSource: RxCollectionViewSectionedDataSource<NumberSection>) {
dataSource.cellFactory = { (cv, ip, i) in
let cell = cv.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: ip) as! NumberCell
cell.value!.text = "\(i)"
return cell
}
dataSource.supplementaryViewFactory = { [unowned dataSource] (cv, kind, ip) in
let section = cv.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "Section", forIndexPath: ip) as! NumberSectionView
section.value!.text = "\(dataSource.sectionAtIndex(ip.section).model)"
return section
}
}
override func viewWillDisappear(animated: Bool) {
self.timer?.invalidate()
}

View File

@ -18,7 +18,7 @@ infix operator <-> {
}
func <-> <T>(property: ControlProperty<T>, variable: Variable<T>) -> Disposable {
let bindToUIDisposable = variable
let bindToUIDisposable = variable.asObservable()
.bindTo(property)
let bindToVariable = property
.subscribe(onNext: { n in

View File

@ -44,7 +44,7 @@ class ActivityIndicator : DriverConvertibleType {
private let _loading: Driver<Bool>
init() {
_loading = _variable
_loading = _variable.asObservable()
.map { $0 > 0 }
.distinctUntilChanged()
.asDriver { (error: ErrorType) -> Driver<Bool> in

View File

@ -11,9 +11,13 @@ import Foundation
/**
Variable is a wrapper for `BehaviorSubject`.
Unlike `BehaviorSubject` it can't terminate with error.
Unlike `BehaviorSubject` it can't terminate with error, and when variable is deallocated
it will complete it's observable sequence (`asObservable`).
*/
public class Variable<Element> : ObservableType {
@available(*, deprecated=2.0.0, message="Variable will remain in the 2.0.0 API, but just use `variable.asObservable()` because it won't be `ObservableType` (no way to warn about deprecated interface implementation). Just do, `variable.asObservable().map { _ in ...}` and ignore this warning.")
public class Variable<Element>
: ObservableType { // << -- this part and subscribe method will be deprecated
public typealias E = Element
private let _subject: BehaviorSubject<Element>
@ -73,4 +77,8 @@ public class Variable<Element> : ObservableType {
public func asObservable() -> Observable<E> {
return _subject
}
deinit {
_subject.on(.Completed)
}
}

View File

@ -172,7 +172,8 @@ extension ControlTests {
// UILabel
extension ControlTests {
func testLabel_HasWeakReference() {
ensureControlObserverHasWeakReference(UILabel(), { (label: UILabel) -> AnyObserver<NSAttributedString?> in label.rx_attributedText }, { Variable<NSAttributedString?>(nil).asObservable() })
let variable = Variable<NSAttributedString?>(nil)
ensureControlObserverHasWeakReference(UILabel(), { (label: UILabel) -> AnyObserver<NSAttributedString?> in label.rx_attributedText }, { variable.asObservable() })
}
func testLabel_NextElementsSetsValue() {

View File

@ -26,7 +26,7 @@ class ControlTests : RxTest {
let property = propertySelector(control)
let disposable = variable.bindTo(property)
let disposable = variable.asObservable().bindTo(property)
_ = property.subscribe(onNext: { n in
lastReturnedPropertyValue = n

View File

@ -15,7 +15,7 @@ class VariableTest : RxTest {
let a = Variable(1)
let b = Variable(2)
let c = Observable.combineLatest(a, b, resultSelector: +)
let c = Observable.combineLatest(a.asObservable(), b.asObservable(), resultSelector: +)
var latestValue: Int?
@ -41,6 +41,26 @@ class VariableTest : RxTest {
XCTAssertEqual(latestValue!, 14)
}
func testVariable_sendsCompletedOnDealloc() {
var a = Variable(1)
var latest = 0
var completed = false
a.asObservable().subscribe(onNext: { n in
latest = n
}, onCompleted: {
completed = true
})
XCTAssertEqual(latest, 1)
XCTAssertFalse(completed)
a = Variable(2)
XCTAssertEqual(latest, 1)
XCTAssertTrue(completed)
}
func testVariable_READMEExample() {
// Two simple Rx variables
@ -49,7 +69,7 @@ class VariableTest : RxTest {
let b /*: Observable<Int>*/ = Variable(2)
// Computed third variable (or sequence)
let c /*: Observable<Int>*/ = Observable.combineLatest(a, b) { $0 + $1 }
let c /*: Observable<Int>*/ = Observable.combineLatest(a.asObservable(), b.asObservable()) { $0 + $1 }
// Reading elements from c.
// This is just a demo example.