Introduces new units `ControlProperty` and `ControlEvent`.

This commit is contained in:
Krunoslav Zaher 2015-08-29 21:20:44 +02:00
parent fbe038fcb2
commit a4219416cd
44 changed files with 1345 additions and 1069 deletions

View File

@ -274,6 +274,12 @@
C8093F5F1B8A73A20088E94D /* Observable+Blocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093F581B8A73A20088E94D /* Observable+Blocking.swift */; };
C80D342E1B9245A40014629D /* CombineLatest+CollectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D342D1B9245A40014629D /* CombineLatest+CollectionType.swift */; settings = {ASSET_TAGS = (); }; };
C80D342F1B9245A40014629D /* CombineLatest+CollectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D342D1B9245A40014629D /* CombineLatest+CollectionType.swift */; settings = {ASSET_TAGS = (); }; };
C80D338F1B91EF9E0014629D /* Observable+CocoaExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D338E1B91EF9E0014629D /* Observable+CocoaExtensions.swift */; settings = {ASSET_TAGS = (); }; };
C80D33901B91EF9E0014629D /* Observable+CocoaExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D338E1B91EF9E0014629D /* Observable+CocoaExtensions.swift */; settings = {ASSET_TAGS = (); }; };
C80D33981B922FB00014629D /* ControlEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D33931B922FB00014629D /* ControlEvent.swift */; settings = {ASSET_TAGS = (); }; };
C80D33991B922FB00014629D /* ControlEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D33931B922FB00014629D /* ControlEvent.swift */; settings = {ASSET_TAGS = (); }; };
C80D339A1B922FB00014629D /* ControlProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D33941B922FB00014629D /* ControlProperty.swift */; settings = {ASSET_TAGS = (); }; };
C80D339B1B922FB00014629D /* ControlProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D33941B922FB00014629D /* ControlProperty.swift */; settings = {ASSET_TAGS = (); }; };
C88254151B8A752B00B02D69 /* CoreDataEntityEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88253EF1B8A752B00B02D69 /* CoreDataEntityEvent.swift */; };
C88254161B8A752B00B02D69 /* RxCollectionViewReactiveArrayDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88253F11B8A752B00B02D69 /* RxCollectionViewReactiveArrayDataSource.swift */; };
C88254171B8A752B00B02D69 /* RxTableViewReactiveArrayDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88253F21B8A752B00B02D69 /* RxTableViewReactiveArrayDataSource.swift */; };
@ -476,6 +482,9 @@
C8093F581B8A73A20088E94D /* Observable+Blocking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Blocking.swift"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
C8093F591B8A73A20088E94D /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
C80D342D1B9245A40014629D /* CombineLatest+CollectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CombineLatest+CollectionType.swift"; sourceTree = "<group>"; };
C80D338E1B91EF9E0014629D /* Observable+CocoaExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+CocoaExtensions.swift"; sourceTree = "<group>"; };
C80D33931B922FB00014629D /* ControlEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlEvent.swift; sourceTree = "<group>"; };
C80D33941B922FB00014629D /* ControlProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlProperty.swift; sourceTree = "<group>"; };
C88253EF1B8A752B00B02D69 /* CoreDataEntityEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataEntityEvent.swift; sourceTree = "<group>"; };
C88253F11B8A752B00B02D69 /* RxCollectionViewReactiveArrayDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewReactiveArrayDataSource.swift; sourceTree = "<group>"; };
C88253F21B8A752B00B02D69 /* RxTableViewReactiveArrayDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewReactiveArrayDataSource.swift; sourceTree = "<group>"; };
@ -781,10 +790,12 @@
C8093E871B8A732E0088E94D /* _RXKVOObserver.m */,
C8093E881B8A732E0088E94D /* _RXSwizzling.h */,
C8093E891B8A732E0088E94D /* _RXSwizzling.m */,
C80D338E1B91EF9E0014629D /* Observable+CocoaExtensions.swift */,
C8093E8A1B8A732E0088E94D /* CLLocationManager+Rx.swift */,
C8093E8B1B8A732E0088E94D /* DelegateProxy.swift */,
C8093E8C1B8A732E0088E94D /* DelegateProxyType.swift */,
C8093E8D1B8A732E0088E94D /* Logging.swift */,
C80D33911B922FB00014629D /* CocoaUnits */,
C8093E8E1B8A732E0088E94D /* Observables */,
C8093E991B8A732E0088E94D /* Proxies */,
C8093E9B1B8A732E0088E94D /* RxCocoa.swift */,
@ -847,6 +858,15 @@
path = RxBlocking;
sourceTree = "<group>";
};
C80D33911B922FB00014629D /* CocoaUnits */ = {
isa = PBXGroup;
children = (
C80D33931B922FB00014629D /* ControlEvent.swift */,
C80D33941B922FB00014629D /* ControlProperty.swift */,
);
path = CocoaUnits;
sourceTree = "<group>";
};
C88253EE1B8A752B00B02D69 /* iOS */ = {
isa = PBXGroup;
children = (
@ -1208,6 +1228,7 @@
C8093EFB1B8A732E0088E94D /* RxCocoa.swift in Sources */,
C88254231B8A752B00B02D69 /* RxTableViewDelegateProxy.swift in Sources */,
C882542D1B8A752B00B02D69 /* UIImageView+Rx.swift in Sources */,
C80D33981B922FB00014629D /* ControlEvent.swift in Sources */,
C8093EF31B8A732E0088E94D /* NSObject+Rx+CoreGraphics.swift in Sources */,
C882542A1B8A752B00B02D69 /* UIControl+Rx.swift in Sources */,
C88254341B8A752B00B02D69 /* UITableView+Rx.swift in Sources */,
@ -1221,6 +1242,7 @@
C88254151B8A752B00B02D69 /* CoreDataEntityEvent.swift in Sources */,
C88254211B8A752B00B02D69 /* RxSearchBarDelegateProxy.swift in Sources */,
C882541D1B8A752B00B02D69 /* RxAlertViewDelegateProxy.swift in Sources */,
C80D338F1B91EF9E0014629D /* Observable+CocoaExtensions.swift in Sources */,
C88254311B8A752B00B02D69 /* UISegmentedControl+Rx.swift in Sources */,
C8093EED1B8A732E0088E94D /* KVOObservable.swift in Sources */,
C88254281B8A752B00B02D69 /* UIButton+Rx.swift in Sources */,
@ -1232,6 +1254,7 @@
C88254241B8A752B00B02D69 /* RxTextViewDelegateProxy.swift in Sources */,
C88254271B8A752B00B02D69 /* UIBarButtonItem+Rx.swift in Sources */,
C88254251B8A752B00B02D69 /* UIActionSheet+Rx.swift in Sources */,
C80D339A1B922FB00014629D /* ControlProperty.swift in Sources */,
C882542B1B8A752B00B02D69 /* UIDatePicker+Rx.swift in Sources */,
C88254221B8A752B00B02D69 /* RxTableViewDataSourceProxy.swift in Sources */,
C8093EDD1B8A732E0088E94D /* _RXSwizzling.m in Sources */,
@ -1265,6 +1288,8 @@
C8093EFE1B8A732E0088E94D /* RxTarget.swift in Sources */,
C8093ED21B8A732E0088E94D /* _RX.m in Sources */,
C8093EFC1B8A732E0088E94D /* RxCocoa.swift in Sources */,
C80D33991B922FB00014629D /* ControlEvent.swift in Sources */,
C80D339B1B922FB00014629D /* ControlProperty.swift in Sources */,
C8093EF41B8A732E0088E94D /* NSObject+Rx+CoreGraphics.swift in Sources */,
C8093EF01B8A732E0088E94D /* KVOObserver.swift in Sources */,
C8093EEE1B8A732E0088E94D /* KVOObservable.swift in Sources */,
@ -1282,6 +1307,7 @@
C8093EF81B8A732E0088E94D /* NSURLSession+Rx.swift in Sources */,
C8093F4C1B8A732E0088E94D /* NSSlider+Rx.swift in Sources */,
C8093EE81B8A732E0088E94D /* ControlTarget.swift in Sources */,
C80D33901B91EF9E0014629D /* Observable+CocoaExtensions.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -0,0 +1,50 @@
//
// ControlEvent.swift
// Rx
//
// Created by Krunoslav Zaher on 8/28/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation
#if !RX_NO_MODULE
import RxSwift
#endif
protocol ControlEventType : ObservableType {
typealias E
func asControlEvent() -> ControlEvent<E>
}
/**
Unit for `Observable`/`ObservableType` that represents event on UI element.
It's properties are:
- it never fails
- it won't send any initial value on subscription
- it will `Complete` sequence on control being deallocated
- it never errors out
- it delivers events on `MainScheduler.sharedInstance`
*/
public struct ControlEvent<PropertyType> : ControlEventType {
public typealias E = PropertyType
let source: Observable<PropertyType>
init(source: Observable<PropertyType>) {
self.source = source
}
public func subscribe<O : ObserverType where O.E == E>(observer: O) -> Disposable {
return self.source.subscribe(observer)
}
public func asObservable() -> Observable<E> {
return self.source
}
public func asControlEvent() -> ControlEvent<E> {
return self
}
}

View File

@ -0,0 +1,63 @@
//
// ControlProperty.swift
// Rx
//
// Created by Krunoslav Zaher on 8/28/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation
#if !RX_NO_MODULE
import RxSwift
#endif
public protocol ControlPropertyType : ObservableType, ObserverType {
func asControlProperty() -> ControlProperty<E>
}
/**
Unit for `Observable`/`ObservableType` that represents property of UI element.
It's properties are:
- it never fails
- `shareReplay(1)` behavior
- it's stateful, upon subscription (calling subscribe) last element is immediatelly replayed if it was produced
- it will `Complete` sequence on control being deallocated
- it never errors out
- it delivers events on `MainScheduler.sharedInstance`
*/
public struct ControlProperty<PropertyType> : ControlPropertyType {
public typealias E = PropertyType
let source: Observable<PropertyType>
let observer: ObserverOf<PropertyType>
init(source: Observable<PropertyType>, observer: ObserverOf<PropertyType>) {
self.source = source
self.observer = observer
}
public func subscribe<O : ObserverType where O.E == E>(observer: O) -> Disposable {
return self.source.subscribe(observer)
}
public func asObservable() -> Observable<E> {
return self.source
}
public func asControlProperty() -> ControlProperty<E> {
return self
}
public func on(event: Event<E>) {
switch event {
case .Error(let error):
bindingErrorToInterface(error)
case .Next:
self.observer.on(event)
case .Completed:
self.observer.on(event)
}
}
}

View File

@ -170,9 +170,7 @@ extension ObservableType {
switch event {
case .Error(let error):
#if DEBUG
rxFatalError("Binding error to data source: \(error)")
#endif
bindingErrorToInterface(error)
disposable.dispose()
case .Completed:
disposable.dispose()

View File

@ -0,0 +1,28 @@
//
// Observable+Extensions.swift
// Rx
//
// Created by Krunoslav Zaher on 8/29/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation
#if !RX_NO_MODULE
import RxSwift
#endif
extension ObservableType {
public func bindTo<O: ObserverType where O.E == E>(target: O) -> Disposable {
return self.subscribe(target)
}
public func bindTo<R>(binder: Self -> R) -> R {
return binder(self)
}
public func bindTo<R1, R2>(binder: Self -> R1 -> R2, curriedArgument: R1) -> R2 {
return binder(self)(curriedArgument)
}
}

View File

@ -103,7 +103,7 @@ extension NSURLSession {
return rx_response(request).map { (data, response) -> NSData in
if let response = response as? NSHTTPURLResponse {
if 200 ..< 300 ~= response.statusCode {
return data!
return data ?? NSData()
}
else {
throw rxError(.NetworkError, message: "Server returned failure", userInfo: [RxCocoaErrorHTTPResponseKey: response])
@ -119,7 +119,7 @@ extension NSURLSession {
public func rx_JSON(request: NSURLRequest) -> Observable<AnyObject!> {
return rx_data(request).map { (data) -> AnyObject! in
return try NSJSONSerialization.JSONObjectWithData(data, options: [])
return try NSJSONSerialization.JSONObjectWithData(data ?? NSData(), options: [])
}
}

View File

@ -51,19 +51,11 @@ func handleVoidObserverResult(result: RxResult<Void>) {
}
func bindingErrorToInterface(error: ErrorType) {
#if DEBUG
rxFatalError("Binding error to UI: \(error)")
#endif
}
// There are certain kinds of errors that shouldn't be silenced, but it could be weird to crash the app because of them.
// DEBUG -> crash the app
// RELEASE -> log to console
func rxPossiblyFatalError(error: String) {
let error = "Binding error to UI: \(error)"
#if DEBUG
rxFatalError(error)
#else
print("[RxSwift]: \(error)")
print(error)
#endif
}

View File

@ -13,7 +13,7 @@ import RxSwift
import Cocoa
extension NSButton {
public var rx_tap: Observable<Void> {
public var rx_tap: ControlEvent<Void> {
return rx_controlEvents
}
}

View File

@ -14,8 +14,8 @@ import RxSwift
extension NSControl {
public var rx_controlEvents: Observable<Void> {
return AnonymousObservable { observer in
public var rx_controlEvents: ControlEvent<Void> {
let source: Observable<Void> = AnonymousObservable { observer in
MainScheduler.ensureExecutingOnScheduler()
let observer = ControlTarget(control: self) { control in
@ -23,19 +23,32 @@ extension NSControl {
}
return observer
}
}.takeUntil(rx_deallocated)
return ControlEvent(source: source)
}
func rx_value<T>(getValue: () -> T) -> Observable<T> {
return AnonymousObservable { observer in
sendNext(observer, getValue())
func rx_value<T>(getter getter: () -> T, setter: T -> Void) -> ControlProperty<T> {
let source: Observable<T> = AnonymousObservable { observer in
sendNext(observer, getter())
let observer = ControlTarget(control: self) { control in
sendNext(observer, getValue())
sendNext(observer, getter())
}
return observer
}
}.takeUntil(rx_deallocated)
return ControlProperty(source: source, observer: ObserverOf { event in
switch event {
case .Next(let value):
setter(value)
case .Error(let error):
bindingErrorToInterface(error)
case .Completed:
break
}
})
}
}

View File

@ -13,37 +13,36 @@ import RxSwift
import Cocoa
extension NSImageView {
public func rx_subscribeImageTo(source: Observable<NSImage?>) -> Disposable {
return rx_subscribeImageTo(false)(source: source)
public var rx_image: ObserverOf<NSImage!> {
return self.rx_imageAnimated(false)
}
public func rx_subscribeImageTo
(animated: Bool)
(source: Observable<NSImage?>) -> Disposable {
public func rx_imageAnimated(animated: Bool) -> ObserverOf<NSImage!> {
return ObserverOf { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
return source.subscribe(AnonymousObserver { event in
switch event {
case .Next(let value):
if animated && value != nil {
let transition = CATransition()
transition.duration = 0.25
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = kCATransitionFade
self.layer!.addAnimation(transition, forKey: kCATransition)
}
else {
self.layer!.removeAllAnimations()
}
self.image = value
case .Error(let error):
#if DEBUG
rxFatalError("Binding error to textbox: \(error)")
#endif
break
case .Completed:
break
switch event {
case .Next(let boxedValue):
let value = boxedValue
if animated && value != nil {
let transition = CATransition()
transition.duration = 0.25
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = kCATransitionFade
self?.layer?.addAnimation(transition, forKey: kCATransition)
}
})
else {
self?.layer?.removeAllAnimations()
}
self?.image = value
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
}
}
}

View File

@ -13,11 +13,13 @@ import RxSwift
import Cocoa
extension NSSlider {
public var rx_value: Observable<Double> {
return rx_value { [weak self] in
public var rx_value: ControlProperty<Double> {
return rx_value(getter: { [weak self] in
return self?.doubleValue ?? 0
}
}, setter: { [weak self] value in
self?.doubleValue = value
})
}
}

View File

@ -39,40 +39,28 @@ class RxTextFieldDelegate : DelegateProxy
let textField: NSTextField = castOrFatalError(object)
textField.delegate = castOptionalOrFatalError(delegate)
}
}
extension ObservableType where E == String {
public func subscribeTextOf(textField: NSTextField) -> Disposable {
return self.subscribe { event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
textField.stringValue = value
case .Error(let error):
#if DEBUG
rxFatalError("Binding error to textbox: \(error)")
#endif
break
case .Completed:
break
}
}
}
}
extension NSTextField {
public func rx_subscribeTextTo(source: Observable<String>) -> Disposable {
return source.subscribe(AnonymousObserver { event in
public var rx_delegate: DelegateProxy {
return proxyForObject(self) as RxTextFieldDelegate
}
public var rx_text: ControlProperty<String> {
let delegate = proxyForObject(self) as RxTextFieldDelegate
let source = delegate.textSubject
return ControlProperty(source: source, observer: ObserverOf { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
self.stringValue = value
self?.stringValue = value
case .Error(let error):
#if DEBUG
rxFatalError("Binding error to textbox: \(error)")
#endif
bindingErrorToInterface(error)
break
case .Completed:
break
@ -80,13 +68,4 @@ extension NSTextField {
})
}
public var rx_delegate: DelegateProxy {
return proxyForObject(self) as RxTextFieldDelegate
}
public var rx_text: Observable<String> {
let delegate = proxyForObject(self) as RxTextFieldDelegate
return delegate.textSubject
}
}

View File

@ -18,24 +18,30 @@ extension UIActionSheet {
return proxyForObject(self) as RxActionSheetDelegateProxy
}
public var rx_clickedButtonAtIndex: Observable<Int> {
return rx_delegate.observe("actionSheet:clickedButtonAtIndex:")
public var rx_clickedButtonAtIndex: ControlEvent<Int> {
let source = rx_delegate.observe("actionSheet:clickedButtonAtIndex:")
.map { a in
return a[1] as! Int
}
return ControlEvent(source: source)
}
public var rx_willDismissWithButtonIndex: Observable<Int> {
return rx_delegate.observe("actionSheet:willDismissWithButtonIndex:")
public var rx_willDismissWithButtonIndex: ControlEvent<Int> {
let source = rx_delegate.observe("actionSheet:willDismissWithButtonIndex:")
.map { a in
return a[1] as! Int
}
return ControlEvent(source: source)
}
public var rx_didDismissWithButtonIndex: Observable<Int> {
return rx_delegate.observe("actionSheet:didDismissWithButtonIndex:")
public var rx_didDismissWithButtonIndex: ControlEvent<Int> {
let source = rx_delegate.observe("actionSheet:didDismissWithButtonIndex:")
.map { a in
return a[1] as! Int
}
return ControlEvent(source: source)
}
}

View File

@ -18,24 +18,30 @@ extension UIAlertView {
return proxyForObject(self) as RxAlertViewDelegateProxy
}
public var rx_clickedButtonAtIndex: Observable<Int> {
return rx_delegate.observe("alertView:clickedButtonAtIndex:")
public var rx_clickedButtonAtIndex: ControlEvent<Int> {
let source = rx_delegate.observe("alertView:clickedButtonAtIndex:")
.map { a in
return a[1] as! Int
}
return ControlEvent(source: source)
}
public var rx_willDismissWithButtonIndex: Observable<Int> {
return rx_delegate.observe("alertView:willDismissWithButtonIndex:")
public var rx_willDismissWithButtonIndex: ControlEvent<Int> {
let source = rx_delegate.observe("alertView:willDismissWithButtonIndex:")
.map { a in
return a[1] as! Int
}
return ControlEvent(source: source)
}
public var rx_didDismissWithButtonIndex: Observable<Int> {
return rx_delegate.observe("alertView:didDismissWithButtonIndex:")
public var rx_didDismissWithButtonIndex: ControlEvent<Int> {
let source = rx_delegate.observe("alertView:didDismissWithButtonIndex:")
.map { a in
return a[1] as! Int
}
return ControlEvent(source: source)
}
}

View File

@ -13,13 +13,15 @@ import RxSwift
extension UIBarButtonItem {
public var rx_tap: Observable<Void> {
return AnonymousObservable { observer in
public var rx_tap: ControlEvent<Void> {
let source: Observable<Void> = AnonymousObservable { observer in
let target = BarButtonItemTarget(barButtonItem: self) {
sendNext(observer, ())
}
return target
} .takeUntil(rx_deallocated)
}.takeUntil(rx_deallocated)
return ControlEvent(source: source)
}
}

View File

@ -13,7 +13,9 @@ import RxSwift
import UIKit
extension UIButton {
public var rx_tap: Observable<Void> {
public var rx_tap: ControlEvent<Void> {
return rx_controlEvents(.TouchUpInside)
}
}

View File

@ -12,45 +12,43 @@ import RxSwift
#endif
import UIKit
extension ObservableType {
// data source
// Registers reactive data source with collection view.
// Difference between reactive data source and UICollectionViewDataSource is that reactive
// has additional method:
//
// ```
// func collectionView(collectionView: UICollectionView, observedEvent: Event<Element>) -> Void
// ```
//
// If you want to register non reactive data source, please use `rx_setDataSource` method
public func subscribe<DataSource: protocol<RxCollectionViewDataSourceType, UICollectionViewDataSource> where E == DataSource.Element>(collectionView: UICollectionView, withReactiveDataSource dataSource: DataSource)
-> Disposable {
return self.subscribeProxyDataSourceForObject(collectionView, dataSource: dataSource, retainDataSource: false) { (_: RxCollectionViewDataSourceProxy, event) -> Void in
dataSource.collectionView(collectionView, observedEvent: event)
}
}
}
// Items
extension ObservableType where E: SequenceType {
// `reloadData` - items subscription methods (it's assumed that there is one section, and it is typed `Void`)
extension UICollectionView {
public func subscribeItemsOf(collectionView: UICollectionView, cellFactory: (UICollectionView, Int, E.Generator.Element) -> UICollectionViewCell)
public func rx_itemsWithCellFactory<S: SequenceType, O: ObservableType where O.E == S>
(source: O)
(cellFactory: (UICollectionView, Int, S.Generator.Element) -> UICollectionViewCell)
-> Disposable {
let dataSource = RxCollectionViewReactiveArrayDataSourceSequenceWrapper<E>(cellFactory: cellFactory)
return self.subscribe(collectionView, withReactiveDataSource: dataSource)
let dataSource = RxCollectionViewReactiveArrayDataSourceSequenceWrapper<S>(cellFactory: cellFactory)
return self.rx_itemsWithDataSource(dataSource)(source: source)
}
public func subscribeItemsOf<Cell: UICollectionViewCell>(collectionView: UICollectionView, withCellIdentifier cellIdentifier: String, configureCell: (Int, E.Generator.Element, Cell) -> Void)
public func rx_itemsWithCellIdentifier<S: SequenceType, Cell: UICollectionViewCell, O : ObservableType where O.E == S>
(cellIdentifier: String)
(source: O)
(configureCell: (Int, S.Generator.Element, Cell) -> Void)
-> Disposable {
let dataSource = RxCollectionViewReactiveArrayDataSourceSequenceWrapper<E> { (cv, i, item) in
let dataSource = RxCollectionViewReactiveArrayDataSourceSequenceWrapper<S> { (cv, i, item) in
let indexPath = NSIndexPath(forItem: i, inSection: 0)
let cell = cv.dequeueReusableCellWithReuseIdentifier(cellIdentifier, forIndexPath: indexPath) as! Cell
configureCell(i, item, cell)
return cell
}
return self.subscribe(collectionView, withReactiveDataSource: dataSource)
return self.rx_itemsWithDataSource(dataSource)(source: source)
}
public func rx_itemsWithDataSource<DataSource: protocol<RxCollectionViewDataSourceType, UICollectionViewDataSource>, S: SequenceType, O: ObservableType where DataSource.Element == S, O.E == S>
(dataSource: DataSource)
(source: O)
-> Disposable {
return source.subscribeProxyDataSourceForObject(self, dataSource: dataSource, retainDataSource: false) { [weak self] (_: RxCollectionViewDataSourceProxy, event) -> Void in
guard let collectionView = self else {
return
}
dataSource.collectionView(collectionView, observedEvent: event)
}
}
}
@ -79,20 +77,24 @@ extension UICollectionView {
// events
public var rx_itemSelected: Observable<NSIndexPath> {
return rx_delegate.observe("collectionView:didSelectItemAtIndexPath:")
public var rx_itemSelected: ControlEvent<NSIndexPath> {
let source = rx_delegate.observe("collectionView:didSelectItemAtIndexPath:")
.map { a in
return a[1] as! NSIndexPath
}
return ControlEvent(source: source)
}
// typed events
public func rx_modelSelected<T>() -> Observable<T> {
return rx_itemSelected .map { indexPath in
public func rx_modelSelected<T>() -> ControlEvent<T> {
let source: Observable<T> = rx_itemSelected .map { indexPath in
let dataSource: RxCollectionViewReactiveArrayDataSource<T> = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_subscribeItemsTo` methods was used.")
return dataSource.modelAtIndex(indexPath.item)!
}
return ControlEvent(source: source)
}
}

View File

@ -12,30 +12,25 @@ import RxSwift
#endif
import UIKit
extension ObservableType where E == Bool {
public func subscribeEnabledOf(control: UIControl) -> Disposable {
weak var weakControl: UIControl? = control
return self.subscribe { event in
extension UIControl {
public var rx_enabled: ObserverOf<Bool> {
return ObserverOf { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
weakControl?.enabled = value
self?.enabled = value
case .Error(let error):
#if DEBUG
rxFatalError("Binding error to textbox: \(error)")
#endif
bindingErrorToInterface(error)
break
case .Completed:
break
}
}
}
}
extension UIControl {
public func rx_controlEvents(controlEvents: UIControlEvents) -> Observable<Void> {
return AnonymousObservable { observer in
public func rx_controlEvents(controlEvents: UIControlEvents) -> ControlEvent<Void> {
let source: Observable<Void> = AnonymousObservable { observer in
MainScheduler.ensureExecutingOnScheduler()
let controlTarget = ControlTarget(control: self, controlEvents: controlEvents) {
@ -46,22 +41,38 @@ extension UIControl {
return AnonymousDisposable {
controlTarget.dispose()
}
} .takeUntil(rx_deallocated)
}.takeUntil(rx_deallocated)
return ControlEvent(source: source)
}
func rx_value<T>(getValue: () -> T) -> Observable<T> {
return AnonymousObservable { observer in
func rx_value<T>(getter getter: () -> T, setter: T -> Void) -> ControlProperty<T> {
let source: Observable<T> = AnonymousObservable { observer in
sendNext(observer, getValue())
sendNext(observer, getter())
let controlTarget = ControlTarget(control: self, controlEvents: UIControlEvents.EditingChanged.union(.ValueChanged)) { control in
sendNext(observer, getValue())
sendNext(observer, getter())
}
return AnonymousDisposable {
controlTarget.dispose()
}
} .takeUntil(rx_deallocated)
}.takeUntil(rx_deallocated)
return ControlProperty<T>(source: source, observer: ObserverOf { event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
setter(value)
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
})
}
}

View File

@ -16,8 +16,12 @@ import UIKit
extension UIDatePicker {
public var rx_date: Observable<NSDate> {
return rx_value { [unowned self] in self.date }
public var rx_date: ControlProperty<NSDate> {
return rx_value(getter: { [unowned self] in
self.date
}, setter: { [weak self] value in
self?.date = value
})
}
}

View File

@ -51,17 +51,19 @@ class GestureTarget: RxTarget {
extension UIGestureRecognizer {
public var rx_event: Observable<UIGestureRecognizer> {
return AnonymousObservable { observer in
public var rx_event: ControlEvent<UIGestureRecognizer> {
let source: Observable<UIGestureRecognizer> = AnonymousObservable { [weak self] observer in
MainScheduler.ensureExecutingOnScheduler()
let observer = GestureTarget(self) {
let observer = GestureTarget(self!) {
control in
sendNext(observer, self)
sendNext(observer, self!)
}
return observer
} .takeUntil(rx_deallocated)
}.takeUntil(rx_deallocated)
return ControlEvent(source: source)
}
}

View File

@ -12,13 +12,14 @@ import RxSwift
#endif
import UIKit
extension ObservableType where E == UIImage? {
public func subscribeImageOf(imageView: UIImageView) -> Disposable {
return subscribeImageOf(imageView, animated: false)
extension UIImageView {
public var rx_image: ObserverOf<UIImage!> {
return self.rx_imageAnimated(false)
}
public func subscribeImageOf(imageView: UIImageView, animated: Bool) -> Disposable {
return self.subscribe { event in
public func rx_imageAnimated(animated: Bool) -> ObserverOf<UIImage!> {
return ObserverOf { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
@ -28,12 +29,12 @@ extension ObservableType where E == UIImage? {
transition.duration = 0.25
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = kCATransitionFade
imageView.layer.addAnimation(transition, forKey: kCATransition)
self?.layer.addAnimation(transition, forKey: kCATransition)
}
else {
imageView.layer.removeAllAnimations()
self?.layer.removeAllAnimations()
}
imageView.image = value
self?.image = value
case .Error(let error):
bindingErrorToInterface(error)
break
@ -42,7 +43,5 @@ extension ObservableType where E == UIImage? {
}
}
}
}
extension UIImageView {
}

View File

@ -12,25 +12,22 @@ import RxSwift
#endif
import UIKit
extension ObservableType where E == String {
public func subscribeTextOf(label: UILabel) -> Disposable {
return self.subscribe { event in
extension UILabel {
public var rx_text: ObserverOf<String> {
return ObserverOf { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
label.text = value
self?.text = value
case .Error(let error):
#if DEBUG
rxFatalError("Binding error to textbox: \(error)")
#endif
bindingErrorToInterface(error)
break
case .Completed:
break
}
}
}
}
extension UILabel {
}

View File

@ -28,10 +28,19 @@ extension UIScrollView {
// properties
public var rx_contentOffset: Observable<CGPoint> {
public var rx_contentOffset: ControlProperty<CGPoint> {
let proxy = proxyForObject(self) as RxScrollViewDelegateProxy
return proxy.contentOffsetSubject
return ControlProperty(source: proxy.contentOffsetSubject, observer: ObserverOf { [weak self] event in
switch event {
case .Next(let value):
self?.contentOffset = value
case .Error(let error):
bindingErrorToInterface(error)
case .Completed:
break
}
})
}
// delegate

View File

@ -20,8 +20,8 @@ extension UISearchBar {
return proxyForObject(self) as RxSearchBarDelegateProxy
}
public var rx_searchText: Observable<String> {
return deferred { [weak self] in
public var rx_searchText: ControlProperty<String> {
let source: Observable<String> = deferred { [weak self] in
let text = self?.text ?? ""
return (self?.rx_delegate.observe("searchBar:textDidChange:") ?? empty())
@ -30,5 +30,16 @@ extension UISearchBar {
}
.startWith(text)
}
return ControlProperty(source: source, observer: ObserverOf { [weak self] event in
switch event {
case .Next(let value):
self?.text = value
case .Error(let error):
bindingErrorToInterface(error)
case .Completed:
break
}
})
}
}

View File

@ -14,8 +14,12 @@ import RxSwift
extension UISegmentedControl {
public var rx_value: Observable<Int> {
return rx_value { [unowned self] in self.selectedSegmentIndex }
public var rx_value: ControlProperty<Int> {
return rx_value(getter: { [unowned self] in
self.selectedSegmentIndex
}, setter: { [weak self] value in
self?.selectedSegmentIndex = value
})
}
}

View File

@ -13,7 +13,13 @@ import RxSwift
import UIKit
extension UISlider {
public var rx_value: Observable<Float> {
return rx_value { [unowned self] in self.value }
public var rx_value: ControlProperty<Float> {
return rx_value(getter: { [unowned self] in
self.value
}, setter: { [weak self] value in
self?.value = value
})
}
}

View File

@ -14,8 +14,12 @@ import RxSwift
extension UISwitch {
public var rx_value: Observable<Bool> {
return rx_value { [unowned self] in self.on }
public var rx_value: ControlProperty<Bool> {
return rx_value(getter: { [unowned self] in
return self.on
}, setter: { [weak self] value in
self?.on = value
})
}
}

View File

@ -12,46 +12,43 @@ import RxSwift
#endif
import UIKit
// Items
extension ObservableType {
// data source
// Registers reactive data source with table view.
// Difference between reactive data source and UITableViewDataSource is that reactive
// has additional method:
//
// ```
// func tableView(tableView: UITableView, observedEvent: Event<Element>) -> Void
// ```
//
// If you want to register non reactive data source, please use `rx_setDataSource` method
public func subscribe<DataSource: protocol<RxTableViewDataSourceType, UITableViewDataSource> where E == DataSource.Element>(tableView: UITableView, withReactiveDataSource dataSource: DataSource)
extension UITableView {
public func rx_itemsWithCellFactory<S: SequenceType, O: ObservableType where O.E == S>
(source: O)
(cellFactory: (UITableView, Int, S.Generator.Element) -> UITableViewCell)
-> Disposable {
return self.subscribeProxyDataSourceForObject(tableView, dataSource: dataSource, retainDataSource: false) { (_: RxTableViewDataSourceProxy, event) -> Void in
dataSource.tableView(tableView, observedEvent: event)
}
let dataSource = RxTableViewReactiveArrayDataSourceSequenceWrapper<S>(cellFactory: cellFactory)
return self.rx_itemsWithDataSource(dataSource)(source: source)
}
}
extension ObservableType where E: SequenceType {
// `reloadData` - items subscription methods (it's assumed that there is one section, and it is typed `Void`)
public func subscribeItemsOf(tableView: UITableView, cellFactory: (UITableView, Int, E.Generator.Element) -> UITableViewCell)
public func rx_itemsWithCellIdentifier<S: SequenceType, Cell: UITableViewCell, O : ObservableType where O.E == S>
(cellIdentifier: String)
(source: O)
(configureCell: (Int, S.Generator.Element, Cell) -> Void)
-> Disposable {
let dataSource = RxTableViewReactiveArrayDataSourceSequenceWrapper<E>(cellFactory: cellFactory)
return self.subscribe(tableView, withReactiveDataSource: dataSource)
}
public func subscribeItemsOf<Cell: UITableViewCell>(tableView: UITableView, withCellIdentifier cellIdentifier: String, configureCell: (Int, E.Generator.Element, Cell) -> Void)
-> Disposable {
let dataSource = RxTableViewReactiveArrayDataSourceSequenceWrapper<E> { (tv, i, item) in
let dataSource = RxTableViewReactiveArrayDataSourceSequenceWrapper<S> { (tv, i, item) in
let indexPath = NSIndexPath(forItem: i, inSection: 0)
let cell = tv.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! Cell
configureCell(i, item, cell)
return cell
}
return self.subscribe(tableView, withReactiveDataSource: dataSource)
return self.rx_itemsWithDataSource(dataSource)(source: source)
}
public func rx_itemsWithDataSource<DataSource: protocol<RxTableViewDataSourceType, UITableViewDataSource>, S: SequenceType, O: ObservableType where DataSource.Element == S, O.E == S>
(dataSource: DataSource)
(source: O)
-> Disposable {
return source.subscribeProxyDataSourceForObject(self, dataSource: dataSource, retainDataSource: false) { [weak self] (_: RxTableViewDataSourceProxy, event) -> Void in
guard let tableView = self else {
return
}
dataSource.tableView(tableView, observedEvent: event)
}
}
}
@ -79,48 +76,58 @@ extension UITableView {
// events
public var rx_itemSelected: Observable<NSIndexPath> {
return rx_delegate.observe("tableView:didSelectRowAtIndexPath:")
public var rx_itemSelected: ControlEvent<NSIndexPath> {
let source = rx_delegate.observe("tableView:didSelectRowAtIndexPath:")
.map { a in
return a[1] as! NSIndexPath
}
return ControlEvent(source: source)
}
public var rx_itemInserted: Observable<NSIndexPath> {
return rx_dataSource.observe("tableView:commitEditingStyle:forRowAtIndexPath:")
public var rx_itemInserted: ControlEvent<NSIndexPath> {
let source = rx_dataSource.observe("tableView:commitEditingStyle:forRowAtIndexPath:")
.filter { a in
return UITableViewCellEditingStyle(rawValue: (a[1] as! NSNumber).integerValue) == .Insert
}
.map { a in
return (a[2] as! NSIndexPath)
}
return ControlEvent(source: source)
}
public var rx_itemDeleted: Observable<NSIndexPath> {
return rx_dataSource.observe("tableView:commitEditingStyle:forRowAtIndexPath:")
public var rx_itemDeleted: ControlEvent<NSIndexPath> {
let source = rx_dataSource.observe("tableView:commitEditingStyle:forRowAtIndexPath:")
.filter { a in
return UITableViewCellEditingStyle(rawValue: (a[1] as! NSNumber).integerValue) == .Delete
}
.map { a in
return (a[2] as! NSIndexPath)
}
return ControlEvent(source: source)
}
public var rx_itemMoved: Observable<ItemMovedEvent> {
return rx_dataSource.observe("tableView:moveRowAtIndexPath:toIndexPath:")
public var rx_itemMoved: ControlEvent<ItemMovedEvent> {
let source: Observable<ItemMovedEvent> = rx_dataSource.observe("tableView:moveRowAtIndexPath:toIndexPath:")
.map { a in
return ((a[1] as! NSIndexPath), (a[2] as! NSIndexPath))
}
return ControlEvent(source: source)
}
// typed events
// This method only works in case one of the `rx_subscribeItemsTo` methods was used.
public func rx_modelSelected<T>() -> Observable<T> {
return rx_itemSelected .map { ip in
public func rx_modelSelected<T>() -> ControlEvent<T> {
let source: Observable<T> = rx_itemSelected.map { ip in
let dataSource: RxTableViewReactiveArrayDataSource<T> = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_subscribeItemsTo` methods was used.")
return dataSource.modelAtIndex(ip.item)!
}
return ControlEvent(source: source)
}
}

View File

@ -13,9 +13,13 @@ import RxSwift
import UIKit
extension UITextField {
public var rx_text: Observable<String> {
return rx_value { [weak self] in
public var rx_text: ControlProperty<String> {
return rx_value(getter: { [weak self] in
self?.text ?? ""
}
}, setter: { [weak self] value in
self?.text = value
})
}
}

View File

@ -18,15 +18,26 @@ extension UITextView {
return RxTextViewDelegateProxy(parentObject: self)
}
public var rx_text: Observable<String> {
return deferred { [weak self] in
public var rx_text: ControlProperty<String> {
let source: Observable<String> = deferred { [weak self] in
let text = self?.text ?? ""
return (self?.rx_delegate.observe("textViewDidChange:") ?? empty())
.map { a in
return (a[0] as? UITextView)?.text ?? ""
}
.startWith(text)
}
}
return ControlProperty(source: source, observer: ObserverOf { [weak self] event in
switch event {
case .Next(let value):
self?.text = value
case .Error(let error):
bindingErrorToInterface(error)
case .Completed:
break
}
})
}
}

View File

@ -36,10 +36,8 @@ class RxCollectionViewSectionedAnimatedDataSource<S: SectionModelType> : RxColle
setSections(c.finalSections)
collectionView.performBatchUpdates(c)
}
case .Error:
#if DEBUG
fatalError("Binding error to UI")
#endif
case .Error(let error):
bindingErrorToInterface(error)
case .Completed:
break
}

View File

@ -22,10 +22,8 @@ class RxCollectionViewSectionedReloadDataSource<S: SectionModelType> : RxCollect
case .Next(let element):
setSections(element)
collectionView.reloadData()
case .Error:
#if DEBUG
fatalError("Binding error to UI")
#endif
case .Error(let error):
bindingErrorToInterface(error)
case .Completed:
break
}

View File

@ -25,10 +25,8 @@ class RxTableViewSectionedAnimatedDataSource<S: SectionModelType> : RxTableViewS
setSections(c.finalSections)
tableView.performBatchUpdates(c)
}
case .Error(_):
#if DEBUG
fatalError("Binding error to UI")
#endif
case .Error(let error):
bindingErrorToInterface(error)
case .Completed:
break
}

View File

@ -22,10 +22,8 @@ class RxTableViewSectionedReloadDataSource<S: SectionModelType> : RxTableViewSec
case .Next(let element):
setSections(element)
tableView.reloadData()
case .Error(_):
#if DEBUG
fatalError("Binding error to UI")
#endif
case .Error(let error):
bindingErrorToInterface(error)
case .Completed:
break
}

View File

@ -0,0 +1,21 @@
//
// RxDataSourceStarterKit.swift
// RxExample
//
// Created by Krunoslav Zaher on 8/29/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation
#if !RX_NO_MODULE
func bindingErrorToInterface(error: ErrorType) {
let error = "Binding error to UI: \(error)"
#if DEBUG
fatalError(error)
#else
print(error)
#endif
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -36,7 +36,7 @@ class IntroductionExampleViewController : ViewController {
.map { (a, b) in
return "\(a + b)"
}
.subscribeTextOf(c)
.bindTo(c.rx_text)
.addDisposableTo(disposeBag)
// Also, tell it out loud

View File

@ -128,11 +128,11 @@ class PartialUpdatesViewController : ViewController {
.startWith(initialState)
updates
.subscribe(partialUpdatesTableViewOutlet, withReactiveDataSource: tvAnimatedDataSource)
.bindTo(partialUpdatesTableViewOutlet.rx_itemsWithDataSource(tvAnimatedDataSource))
.addDisposableTo(disposeBag)
self.sections
.subscribe(reloadTableViewOutlet, withReactiveDataSource: reloadDataSource)
.bindTo(reloadTableViewOutlet.rx_itemsWithDataSource(reloadDataSource))
.addDisposableTo(disposeBag)
// Collection view logic works, but when clicking fast because of internal bugs
@ -147,18 +147,18 @@ class PartialUpdatesViewController : ViewController {
// While `useAnimatedUpdateForCollectionView` is false, you can click as fast as
// you want, table view doesn't seem to have same issues like collection view.
#if useAnimatedUpdateForCollectionView
#if useAnimatedUpdateForCollectionView
let cvAnimatedDataSource = RxCollectionViewSectionedAnimatedDataSource<NumberSection>()
skinCollectionViewDataSource(cvAnimatedDataSource)
updates
.partialUpdatesCollectionViewOutlet.rx_subscribeWithReactiveDataSource(cvAnimatedDataSource)
.disposeBag.addDisposable
.bindTo(partialUpdatesCollectionViewOutlet.rx_itemsWithDataSource(cvAnimatedDataSource))
.addDisposableTo(disposeBag)
#else
let cvReloadDataSource = RxCollectionViewSectionedReloadDataSource<NumberSection>()
skinCollectionViewDataSource(cvReloadDataSource)
self.sections
.subscribe(partialUpdatesCollectionViewOutlet, withReactiveDataSource: cvReloadDataSource)
.bindTo(partialUpdatesCollectionViewOutlet.rx_itemsWithDataSource(cvReloadDataSource))
.addDisposableTo(disposeBag)
#endif

View File

@ -36,7 +36,7 @@ class DetailViewController: ViewController {
UIImage(data: data)
}
.observeSingleOn($.mainScheduler)
.subscribeImageOf(imageView)
.subscribe(imageView.rx_image)
.addDisposableTo(disposeBag)
label.text = user.firstName + " " + user.lastName

View File

@ -60,7 +60,7 @@ class TableViewController: ViewController, UITableViewDelegate {
// reactive data source
allUsers
.subscribe(tableView, withReactiveDataSource: dataSource)
.bindTo(tableView.rx_itemsWithDataSource(dataSource))
.addDisposableTo(disposeBag)
// customization using delegate

View File

@ -18,12 +18,12 @@ public class CollectionViewImageCell: UICollectionViewCell {
var disposeBag: DisposeBag!
var image: Observable<UIImage?>! {
var image: Observable<UIImage!>! {
didSet {
let disposeBag = DisposeBag()
self.image
.subscribeImageOf(imageOutlet, animated: true)
.subscribe(imageOutlet.rx_imageAnimated(true))
.addDisposableTo(disposeBag)
self.disposeBag = disposeBag

View File

@ -34,20 +34,20 @@ public class WikipediaSearchCell: UITableViewCell {
let disposeBag = DisposeBag()
(viewModel?.title ?? just(""))
.subscribeTextOf(self.titleOutlet)
.subscribe(self.titleOutlet.rx_text)
.addDisposableTo(disposeBag)
self.URLOutlet.text = viewModel.searchResult.URL.absoluteString ?? ""
viewModel.imageURLs
.subscribeItemsOf(self.imagesOutlet, withCellIdentifier: "ImageCell") { [unowned self] (_, URL, cell: CollectionViewImageCell) in
.bindTo(self.imagesOutlet.rx_itemsWithCellIdentifier("ImageCell")) { [unowned self] (_, URL, cell: CollectionViewImageCell) in
let loadingPlaceholder: UIImage? = nil
cell.image = self.imageService.imageFromURL(URL)
.map { $0 as UIImage? }
.catchErrorResumeNext(nil)
.startWith(loadingPlaceholder)
}
}
.addDisposableTo(disposeBag)
self.disposeBag = disposeBag

View File

@ -33,17 +33,17 @@ class WikipediaSearchViewController: ViewController {
resultsTableView.rowHeight = 194
let selectedResult: Observable<SearchResultViewModel> = resultsTableView.rx_modelSelected()
let selectedResult: Observable<SearchResultViewModel> = resultsTableView.rx_modelSelected().asObservable()
let viewModel = SearchViewModel(
searchText: searchBar.rx_searchText,
searchText: searchBar.rx_searchText.asObservable(),
selectedResult: selectedResult
)
// map table view rows
// {
viewModel.rows
.subscribeItemsOf(resultsTableView, withCellIdentifier: "WikipediaSearchCell") { (_, viewModel, cell: WikipediaSearchCell) in
.bindTo(resultsTableView.rx_itemsWithCellIdentifier("WikipediaSearchCell")) { (_, viewModel, cell: WikipediaSearchCell) in
cell.viewModel = viewModel
}
.addDisposableTo(disposeBag)

View File

@ -15,7 +15,7 @@ class UIControlRxTests : RxTest {
func testSubscribeEnabledToTrue() {
let subject = UIControl()
let enabledSequence = Variable<Bool>(false)
let disposable = enabledSequence.subscribeEnabledOf(subject)
let disposable = enabledSequence.subscribe(subject.rx_enabled)
enabledSequence.sendNext(true)
XCTAssert(subject.enabled == true, "Expected enabled set to true")
@ -24,7 +24,7 @@ class UIControlRxTests : RxTest {
func testSubscribeEnabledToFalse() {
let subject = UIControl()
let enabledSequence = Variable<Bool>(true)
let disposable = enabledSequence.subscribeEnabledOf(subject)
let disposable = enabledSequence.subscribe(subject.rx_enabled)
enabledSequence.sendNext(false)
XCTAssert(subject.enabled == false, "Expected enabled set to false")