Skip to content

Commit

Permalink
Add RxPickerViewDataSourceProxy, RxPickerViewDataSourceType and UIPic…
Browse files Browse the repository at this point in the history
…kerView reactive extensions
  • Loading branch information
sergdort committed Jul 5, 2017
1 parent 38552d5 commit 90cd66a
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 0 deletions.
20 changes: 20 additions & 0 deletions Rx.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@
9BA1CBFD1C0F84A10044B50A /* UIActivityIndicatorView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BA1CBD11C0F7C0A0044B50A /* UIActivityIndicatorView+Rx.swift */; };
9BA1CBFE1C0F84C40044B50A /* UIActivityIndicatorView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BA1CBD11C0F7C0A0044B50A /* UIActivityIndicatorView+Rx.swift */; };
9D71C4D21BF08191006E8F59 /* UIButton+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254061B8A752B00B02D69 /* UIButton+Rx.swift */; };
A520FFF71F0D258E00573734 /* RxPickerViewDataSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A520FFF61F0D258E00573734 /* RxPickerViewDataSourceType.swift */; };
A520FFF81F0D258E00573734 /* RxPickerViewDataSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A520FFF61F0D258E00573734 /* RxPickerViewDataSourceType.swift */; };
A520FFF91F0D258E00573734 /* RxPickerViewDataSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A520FFF61F0D258E00573734 /* RxPickerViewDataSourceType.swift */; };
A520FFFA1F0D258E00573734 /* RxPickerViewDataSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A520FFF61F0D258E00573734 /* RxPickerViewDataSourceType.swift */; };
A520FFFC1F0D291500573734 /* RxPickerViewDataSourceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A520FFFB1F0D291500573734 /* RxPickerViewDataSourceProxy.swift */; };
A520FFFD1F0D291500573734 /* RxPickerViewDataSourceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A520FFFB1F0D291500573734 /* RxPickerViewDataSourceProxy.swift */; };
A520FFFE1F0D291500573734 /* RxPickerViewDataSourceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A520FFFB1F0D291500573734 /* RxPickerViewDataSourceProxy.swift */; };
A520FFFF1F0D291500573734 /* RxPickerViewDataSourceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A520FFFB1F0D291500573734 /* RxPickerViewDataSourceProxy.swift */; };
AAE623761C82475700FC7801 /* UIProgressView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE623751C82475700FC7801 /* UIProgressView+Rx.swift */; };
AAE623771C82475700FC7801 /* UIProgressView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE623751C82475700FC7801 /* UIProgressView+Rx.swift */; };
B44D73EC1EE6D4A300EBFBE8 /* UIViewController+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271A97421CFC99FE00D64125 /* UIViewController+RxTests.swift */; };
Expand Down Expand Up @@ -1701,6 +1709,8 @@
91BE429B1CBF7EC000F6B062 /* UIPageControl+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIPageControl+Rx.swift"; sourceTree = "<group>"; };
9BA1CBD11C0F7C0A0044B50A /* UIActivityIndicatorView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIActivityIndicatorView+Rx.swift"; sourceTree = "<group>"; };
A111CE961B91C97C00D0DCEE /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A520FFF61F0D258E00573734 /* RxPickerViewDataSourceType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxPickerViewDataSourceType.swift; sourceTree = "<group>"; };
A520FFFB1F0D291500573734 /* RxPickerViewDataSourceProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxPickerViewDataSourceProxy.swift; sourceTree = "<group>"; };
AAE623751C82475700FC7801 /* UIProgressView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIProgressView+Rx.swift"; sourceTree = "<group>"; };
C809396D1B8A71760088E94D /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; };
C80939E71B8A71840088E94D /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -2938,6 +2948,7 @@
children = (
C88253F71B8A752B00B02D69 /* RxCollectionViewDataSourceType.swift */,
C88253F81B8A752B00B02D69 /* RxTableViewDataSourceType.swift */,
A520FFF61F0D258E00573734 /* RxPickerViewDataSourceType.swift */,
);
path = Protocols;
sourceTree = "<group>";
Expand All @@ -2959,6 +2970,7 @@
844BC8AA1CE4FA5600F5C7CB /* RxPickerViewDelegateProxy.swift */,
D9080ACD1EA05A16002B433B /* RxNavigationControllerDelegateProxy.swift */,
4613457B1D9A4AEE001ABAF2 /* RxWebViewDelegateProxy.swift */,
A520FFFB1F0D291500573734 /* RxPickerViewDataSourceProxy.swift */,
);
path = Proxies;
sourceTree = "<group>";
Expand Down Expand Up @@ -3991,6 +4003,7 @@
F31F35B01BB4FED800961002 /* UIStepper+Rx.swift in Sources */,
C89AB1C61DAAC3350065FBE6 /* ControlEvent.swift in Sources */,
C882542D1B8A752B00B02D69 /* UIImageView+Rx.swift in Sources */,
A520FFFC1F0D291500573734 /* RxPickerViewDataSourceProxy.swift in Sources */,
C882542A1B8A752B00B02D69 /* UIControl+Rx.swift in Sources */,
C8D132441C42D15E00B59FFF /* SectionedViewDataSourceType.swift in Sources */,
C89AB1FA1DAAC3350065FBE6 /* Variable+SharedSequence.swift in Sources */,
Expand All @@ -4011,6 +4024,7 @@
54D2138E1CE0824E0028D5B4 /* UINavigationItem+Rx.swift in Sources */,
91BE429C1CBF7EC000F6B062 /* UIPageControl+Rx.swift in Sources */,
C88254211B8A752B00B02D69 /* RxSearchBarDelegateProxy.swift in Sources */,
A520FFF71F0D258E00573734 /* RxPickerViewDataSourceType.swift in Sources */,
7EDBAEBC1C89B9B7006CBE67 /* UITabBarItem+Rx.swift in Sources */,
C839365F1C70E02200A9A09E /* UIApplication+Rx.swift in Sources */,
844BC8AC1CE4FA6300F5C7CB /* RxPickerViewDelegateProxy.swift in Sources */,
Expand Down Expand Up @@ -4080,6 +4094,7 @@
C89AB1FF1DAAC3350065FBE6 /* UIBindingObserver.swift in Sources */,
C8093EE41B8A732E0088E94D /* DelegateProxyType.swift in Sources */,
C86781AB1DB823B500B2029A /* NSTextField+Rx.swift in Sources */,
A520FFF81F0D258E00573734 /* RxPickerViewDataSourceType.swift in Sources */,
C89AB1E31DAAC3350065FBE6 /* Variable+Driver.swift in Sources */,
C89AB1D31DAAC3350065FBE6 /* ControlProperty+Driver.swift in Sources */,
C8A81CA11E05E82C0008DEF4 /* DispatchQueue+Extensions.swift in Sources */,
Expand All @@ -4089,6 +4104,7 @@
C89AB2511DAAC3A60065FBE6 /* _RXObjCRuntime.m in Sources */,
C89AB20B1DAAC3350065FBE6 /* KVORepresentable.swift in Sources */,
C89AB1A71DAAC25A0065FBE6 /* RxCocoaObjCRuntimeError+Extensions.swift in Sources */,
A520FFFD1F0D291500573734 /* RxPickerViewDataSourceProxy.swift in Sources */,
C89AB2411DAAC3A60065FBE6 /* _RXDelegateProxy.m in Sources */,
C89AB1FB1DAAC3350065FBE6 /* Variable+SharedSequence.swift in Sources */,
C867819C1DB823B500B2029A /* NSControl+Rx.swift in Sources */,
Expand Down Expand Up @@ -5178,6 +5194,7 @@
C89AB23B1DAAC3A60065FBE6 /* _RX.m in Sources */,
C8F0C0151BBBFBB9001B112F /* UIControl+Rx.swift in Sources */,
C8A81CA31E05E82C0008DEF4 /* DispatchQueue+Extensions.swift in Sources */,
A520FFFA1F0D258E00573734 /* RxPickerViewDataSourceType.swift in Sources */,
C8D132471C42D15E00B59FFF /* SectionedViewDataSourceType.swift in Sources */,
C8F0C0161BBBFBB9001B112F /* UITableView+Rx.swift in Sources */,
C89AB1D11DAAC3350065FBE6 /* ControlEvent+Driver.swift in Sources */,
Expand All @@ -5201,6 +5218,7 @@
C8F0C01F1BBBFBB9001B112F /* Observable+Bind.swift in Sources */,
C8F0C0201BBBFBB9001B112F /* UISegmentedControl+Rx.swift in Sources */,
C89AB1ED1DAAC3350065FBE6 /* SharedSequence+Operators+arity.swift in Sources */,
A520FFFF1F0D291500573734 /* RxPickerViewDataSourceProxy.swift in Sources */,
C8F0C0221BBBFBB9001B112F /* UIButton+Rx.swift in Sources */,
9BA1CBFE1C0F84C40044B50A /* UIActivityIndicatorView+Rx.swift in Sources */,
C89AB1A91DAAC25A0065FBE6 /* RxCocoaObjCRuntimeError+Extensions.swift in Sources */,
Expand Down Expand Up @@ -5325,6 +5343,7 @@
ECBBA59F1DF8C0D400DDDC2E /* RxTabBarControllerDelegateProxy.swift in Sources */,
842A5A2D1C357F93003568D5 /* NSTextStorage+Rx.swift in Sources */,
D203C5071BB9C53E00D02D00 /* UIDatePicker+Rx.swift in Sources */,
A520FFFE1F0D291500573734 /* RxPickerViewDataSourceProxy.swift in Sources */,
C89AB1E01DAAC3350065FBE6 /* ObservableConvertibleType+Driver.swift in Sources */,
C89AB2241DAAC3350065FBE6 /* URLSession+Rx.swift in Sources */,
D203C50D1BB9C53E00D02D00 /* UISegmentedControl+Rx.swift in Sources */,
Expand All @@ -5336,6 +5355,7 @@
D203C50A1BB9C53E00D02D00 /* UILabel+Rx.swift in Sources */,
C89AB1FC1DAAC3350065FBE6 /* Variable+SharedSequence.swift in Sources */,
D203C4F51BB9C52900D02D00 /* ItemEvents.swift in Sources */,
A520FFF91F0D258E00573734 /* RxPickerViewDataSourceType.swift in Sources */,
C8BCD3F61C14B6D1005F1280 /* NSLayoutConstraint+Rx.swift in Sources */,
D203C4FA1BB9C53700D02D00 /* RxCollectionViewDataSourceProxy.swift in Sources */,
C89AB2141DAAC3350065FBE6 /* NotificationCenter+Rx.swift in Sources */,
Expand Down
28 changes: 28 additions & 0 deletions RxCocoa/iOS/Protocols/RxPickerViewDataSourceType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// RxPickerViewDataSourceType.swift
// RxCocoa
//
// Created by Sergey Shulga on 05/07/2017.
// Copyright © 2017 Krunoslav Zaher. All rights reserved.
//

#if os(iOS) || os(tvOS)

import UIKit
#if !RX_NO_MODULE
import RxSwift
#endif

/// Marks data source as `UIPickerView` reactive data source enabling it to be used with one of the `bindTo` methods.
public protocol RxPickerViewDataSourceType {
/// Type of elements that can be bound to picker view.
associatedtype Element

/// New observable sequence event observed.
///
/// - parameter pickerView: Bound picker view.
/// - parameter observedEvent: Event
func pickerView(_ pickerView: UIPickerView, observedEvent: Event<Element>)
}

#endif
84 changes: 84 additions & 0 deletions RxCocoa/iOS/Proxies/RxPickerViewDataSourceProxy.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//
// RxPickerViewDataSourceProxy.swift
// RxCocoa
//
// Created by Sergey Shulga on 05/07/2017.
// Copyright © 2017 Krunoslav Zaher. All rights reserved.
//

#if os(iOS) || os(tvOS)

import UIKit
#if !RX_NO_MODULE
import RxSwift
#endif

let pickerViewDataSourceNotSet = PickerViewDataSourceNotSet()

final class PickerViewDataSourceNotSet: NSObject, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
rxAbstractMethod()
}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
rxAbstractMethod()
}
}

public class RxPickerViewDataSourceProxy
: DelegateProxy
, UIPickerViewDataSource
, DelegateProxyType {
public weak fileprivate(set) var pickerView: UIPickerView?
private weak var _requiredMethodsDataSource: UIPickerViewDataSource? = pickerViewDataSourceNotSet

public required init(parentObject: AnyObject) {
self.pickerView = castOrFatalError(parentObject)
super.init(parentObject: parentObject)
}


// MARK: UIPickerViewDataSource

public func numberOfComponents(in pickerView: UIPickerView) -> Int {
return (_requiredMethodsDataSource ?? pickerViewDataSourceNotSet).numberOfComponents(in: pickerView)
}

public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return (_requiredMethodsDataSource ?? pickerViewDataSourceNotSet).pickerView(pickerView, numberOfRowsInComponent: component)
}

// MARK: proxy

/// For more information take a look at `DelegateProxyType`.
public override class func createProxyForObject(_ object: AnyObject) -> AnyObject {
let pickerView: UIPickerView = castOrFatalError(object)
return pickerView.createRxDataSourceProxy()
}

/// For more information take a look at `DelegateProxyType`.
public override class func delegateAssociatedObjectTag() -> UnsafeRawPointer {
return dataSourceAssociatedTag
}

/// For more information take a look at `DelegateProxyType`.
public class func setCurrentDelegate(_ delegate: AnyObject?, toObject object: AnyObject) {
let pickerView: UIPickerView = castOrFatalError(object)
pickerView.dataSource = castOptionalOrFatalError(delegate)
}

/// For more information take a look at `DelegateProxyType`.
public class func currentDelegateFor(_ object: AnyObject) -> AnyObject? {
let tableView: UITableView = castOrFatalError(object)
return tableView.dataSource
}

/// For more information take a look at `DelegateProxyType`.
public override func setForwardToDelegate(_ forwardToDelegate: AnyObject?, retainDelegate: Bool) {
let requiredMethodsDataSource: UIPickerViewDataSource? = castOptionalOrFatalError(forwardToDelegate)
_requiredMethodsDataSource = requiredMethodsDataSource ?? pickerViewDataSourceNotSet
super.setForwardToDelegate(forwardToDelegate, retainDelegate: retainDelegate)
}
}

#endif
35 changes: 35 additions & 0 deletions RxCocoa/iOS/UIPickerView+Rx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@
return RxPickerViewDelegateProxy(parentObject: self)
}

/**
Factory method that enables subclasses to implement their own `rx.dataSource`.

- returns: Instance of delegate proxy that wraps `dataSource`.
*/
public func createRxDataSourceProxy() -> RxPickerViewDataSourceProxy {
return RxPickerViewDataSourceProxy(parentObject: self)
}

}

extension Reactive where Base: UIPickerView {
Expand All @@ -32,6 +41,18 @@
return RxPickerViewDelegateProxy.proxyForObject(base)
}

/// Installs delegate as forwarding delegate on `delegate`.
/// Delegate won't be retained.
///
/// It enables using normal delegate mechanism with reactive delegate mechanism.
///
/// - parameter delegate: Delegate object.
/// - returns: Disposable object that can be used to unbind the delegate.
public func setDelegate(_ delegate: UIPickerViewDelegate)
-> Disposable {
return RxPickerViewDelegateProxy.installForwardDelegate(delegate, retainDelegate: false, onProxyForObject: self.base)
}

public var itemSelected: ControlEvent<(Int, Int)> {
let source = delegate
.methodInvoked(#selector(UIPickerViewDelegate.pickerView(_:didSelectRow:inComponent:)))
Expand All @@ -40,6 +61,20 @@
}
return ControlEvent(events: source)
}

public func items<O: ObservableType,
Adapter: RxPickerViewDataSourceType & UIPickerViewDataSource & UIPickerViewDelegate>(adapter: Adapter)
-> (_ source: O)
-> Disposable where O.E == Adapter.Element {
return { source in
let delegateSubscription = self.setDelegate(adapter)
let dataSourceSubscription = source.subscribeProxyDataSource(ofObject: self.base, dataSource: adapter, retainDataSource: true, binding: { [weak pickerView = self.base] (_: RxPickerViewDataSourceProxy, event) in
guard let pickerView = pickerView else { return }
adapter.pickerView(pickerView, observedEvent: event)
})
return Disposables.create(delegateSubscription, dataSourceSubscription)
}
}
}

#endif

0 comments on commit 90cd66a

Please sign in to comment.