Introduces new units `ControlProperty` and `ControlEvent`.
This commit is contained in:
parent
fbe038fcb2
commit
a4219416cd
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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: [])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Reference in New Issue