Merge master into add_rx_tab_bar_controller_delegate_proxy

This commit is contained in:
kitasuke 2016-12-07 23:34:07 -08:00
commit 437c339545
17 changed files with 389 additions and 100 deletions

View File

@ -250,6 +250,7 @@ custom_categories:
- UITextView+Rx
- UIView+Rx
- UIViewController+Rx
- UIWebView+Rx
- name: RxCocoa/iOS/Protocols
children:
- RxCollectionViewDataSourceType
@ -268,6 +269,7 @@ custom_categories:
- RxTableViewDelegateProxy
- RxTextStorageDelegateProxy
- RxTextViewDelegateProxy
- RxWebViewDelegateProxy
- name: RxCocoa/macOS
children:
- NSButton+Rx

View File

@ -7,6 +7,10 @@ All notable changes to this project will be documented in this file.
* Adds `didScroll` and `didZoom` `ControlEvent`s to `UIScrollView+Rx`
* Renames `refreshing` to `isRefreshing`.
* adds `UIWebView` extensions:
* `didStartLoad`
* `didFinishLoad`
* `didFailLoad`
* Adds `willBeginCustomizing`, `willEndCustomizing`, `didEndCustomizing` and `didSelect` to `UITabBarController+Rx`
## [3.0.1](https://github.com/ReactiveX/RxSwift/releases/tag/3.0.1) (Xcode 8 / Swift 3.0 compatible)
@ -44,7 +48,7 @@ All notable changes to this project will be documented in this file.
#### Anomalies
* Fixes wrong casing in `#import "include/_RXObjCRuntime.h"` (was creating issues for people with
* Fixes wrong casing in `#import "include/_RXObjCRuntime.h"` (was creating issues for people with
case sensitive file system). #949
* Fixes issues with locking strategy for subjects. #936
* Fixes code example in comments of RxTableViewExtensions that didn't compile. #947
@ -54,7 +58,7 @@ All notable changes to this project will be documented in this file.
* Renames `RxTests` library to `RxTest` because of problems with Swift Package Manager.
* Adds Swift Package Manager support
* Adds Linux support
* Adds Linux support
* Replaces `AnyObserver` with `UIBindingObserver` in public interface.
* Renames `resourceCount` to `Resources.total`.
* Makes `rx.text` type consistent with UIKit `String?` type.
@ -99,7 +103,7 @@ text.drive(label.rx.text)
* Add `rx.` extensions on Types.
* Moves `UIImagePickerViewController` and `CLLocationManager` out of `RxCocoa` to `RxExample` project because of App Store submissions issues
* Moves `UIImagePickerViewController` and `CLLocationManager` out of `RxCocoa` to `RxExample` project because of App Store submissions issues
on iOS 10.
* Adds `sentMessage` got its equivalent sequence `methodInvoked` that produces elements after method is invoked (vs before method is invoked).
@ -130,7 +134,7 @@ any observers or `forwardToDelegate` wasn't implementing `UITableViewDataSource.
* Update Getting Started document, section on creating an observable that performs work to Swift 3.0.
* Removes stale installation instructions.
* Removes stale installation instructions.
## [3.0.0-beta.1](https://github.com/ReactiveX/RxSwift/releases/tag/3.0.0-beta.1) (Xcode 8 GM compatible 8A218a)
@ -145,6 +149,7 @@ any observers or `forwardToDelegate` wasn't implementing `UITableViewDataSource.
* `textDidEndEditing`
* Moves `CLLocationManager` and `UIImagePickerViewController` extensions from RxCocoa to RxExample project. #874
* Adds matrix CI builds.
=======
## [3.0.0.alpha.1](https://github.com/ReactiveX/RxSwift/releases/tag/3.0.0.alpha.1) (Xcode 8 beta 6 compatible 8S201h)

View File

@ -16,6 +16,9 @@
1AF67DA81CED430100C310FA /* ReplaySubjectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF67DA51CED430100C310FA /* ReplaySubjectTest.swift */; };
271A97411CFC996B00D64125 /* UIViewController+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271A97401CFC996B00D64125 /* UIViewController+Rx.swift */; };
271A97441CFC9F7B00D64125 /* UIViewControler+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271A97421CFC99FE00D64125 /* UIViewControler+RxTests.swift */; };
4613456F1D9A4467001ABAF2 /* UIWebView+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4613456E1D9A4467001ABAF2 /* UIWebView+RxTests.swift */; };
461345711D9A4543001ABAF2 /* UIWebView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 461345701D9A4543001ABAF2 /* UIWebView+Rx.swift */; };
4613457C1D9A4AEE001ABAF2 /* RxWebViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4613457B1D9A4AEE001ABAF2 /* RxWebViewDelegateProxy.swift */; };
46307D4E1CDE77D800E47A1C /* UIAlertAction+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46307D4D1CDE77D800E47A1C /* UIAlertAction+Rx.swift */; };
46307D4F1CDE77D800E47A1C /* UIAlertAction+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46307D4D1CDE77D800E47A1C /* UIAlertAction+Rx.swift */; };
54700CA01CE37E1800EF3A8F /* UINavigationItem+RxTests.swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54700C9E1CE37D1000EF3A8F /* UINavigationItem+RxTests.swift.swift */; };
@ -1459,6 +1462,9 @@
1AF67DA51CED430100C310FA /* ReplaySubjectTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplaySubjectTest.swift; sourceTree = "<group>"; };
271A97401CFC996B00D64125 /* UIViewController+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Rx.swift"; sourceTree = "<group>"; };
271A97421CFC99FE00D64125 /* UIViewControler+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewControler+RxTests.swift"; sourceTree = "<group>"; };
4613456E1D9A4467001ABAF2 /* UIWebView+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIWebView+RxTests.swift"; sourceTree = "<group>"; };
461345701D9A4543001ABAF2 /* UIWebView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIWebView+Rx.swift"; sourceTree = "<group>"; };
4613457B1D9A4AEE001ABAF2 /* RxWebViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxWebViewDelegateProxy.swift; sourceTree = "<group>"; };
46307D4D1CDE77D800E47A1C /* UIAlertAction+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIAlertAction+Rx.swift"; sourceTree = "<group>"; };
54700C9E1CE37D1000EF3A8F /* UINavigationItem+RxTests.swift.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UINavigationItem+RxTests.swift.swift"; sourceTree = "<group>"; };
54D2138C1CE081890028D5B4 /* UINavigationItem+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UINavigationItem+Rx.swift"; sourceTree = "<group>"; };
@ -2363,6 +2369,12 @@
C8F27DB11CE6711600D5FB4F /* UITextView+RxTests.swift */,
C83508F11C38706D0027C24C /* UIView+RxTests.swift */,
271A97421CFC99FE00D64125 /* UIViewControler+RxTests.swift */,
844BC8B71CE5023200F5C7CB /* UIPickerView+RxTests.swift */,
914FCD661CCDB82E0058B304 /* UIPageControl+RxTest.swift */,
033C2EF41D081B2A0050C015 /* UIScrollView+RxTests.swift */,
C8379EF31D1DD326003EF8FC /* UIButton+RxTests.swift */,
C8A9B6F31DAD752200C9B027 /* Observable+BindTests.swift */,
4613456E1D9A4467001ABAF2 /* UIWebView+RxTests.swift */,
);
path = RxCocoaTests;
sourceTree = "<group>";
@ -2567,6 +2579,7 @@
54D2138C1CE081890028D5B4 /* UINavigationItem+Rx.swift */,
844BC8B31CE4FD7500F5C7CB /* UIPickerView+Rx.swift */,
46307D4D1CDE77D800E47A1C /* UIAlertAction+Rx.swift */,
461345701D9A4543001ABAF2 /* UIWebView+Rx.swift */,
);
path = iOS;
sourceTree = "<group>";
@ -2612,6 +2625,7 @@
84C225A21C33F00B008724EC /* RxTextStorageDelegateProxy.swift */,
846436E11C9AF64C0035B40D /* RxSearchControllerDelegateProxy.swift */,
844BC8AA1CE4FA5600F5C7CB /* RxPickerViewDelegateProxy.swift */,
4613457B1D9A4AEE001ABAF2 /* RxWebViewDelegateProxy.swift */,
);
path = Proxies;
sourceTree = "<group>";
@ -3559,6 +3573,7 @@
C882542F1B8A752B00B02D69 /* UIScrollView+Rx.swift in Sources */,
844BC8B41CE4FD7500F5C7CB /* UIPickerView+Rx.swift in Sources */,
C89AB20E1DAAC3350065FBE6 /* Logging.swift in Sources */,
461345711D9A4543001ABAF2 /* UIWebView+Rx.swift in Sources */,
C8093EE31B8A732E0088E94D /* DelegateProxyType.swift in Sources */,
C8093EFD1B8A732E0088E94D /* RxTarget.swift in Sources */,
C88254361B8A752B00B02D69 /* UITextView+Rx.swift in Sources */,
@ -3636,6 +3651,7 @@
C882541A1B8A752B00B02D69 /* RxCollectionViewDataSourceType.swift in Sources */,
C88254351B8A752B00B02D69 /* UITextField+Rx.swift in Sources */,
C89AB1FE1DAAC3350065FBE6 /* UIBindingObserver.swift in Sources */,
4613457C1D9A4AEE001ABAF2 /* RxWebViewDelegateProxy.swift in Sources */,
C88254301B8A752B00B02D69 /* UISearchBar+Rx.swift in Sources */,
C89AB2121DAAC3350065FBE6 /* NSNotificationCenter+Rx.swift in Sources */,
C88254181B8A752B00B02D69 /* ItemEvents.swift in Sources */,
@ -3786,6 +3802,8 @@
C83509361C38706E0027C24C /* NSLayoutConstraint+RxTests.swift in Sources */,
C835095E1C38706E0027C24C /* Observable+StandardSequenceOperatorsTest.swift in Sources */,
C8C4F1631DE9D0A800003FA7 /* UIProgressView+RxTests.swift in Sources */,
4613456F1D9A4467001ABAF2 /* UIWebView+RxTests.swift in Sources */,
C83509691C38706E0027C24C /* Recorded+Timeless.swift in Sources */,
C835094C1C38706E0027C24C /* AssumptionsTest.swift in Sources */,
C835092D1C38706E0027C24C /* Control+RxTests.swift in Sources */,
C83509601C38706E0027C24C /* Observable+TimeTest.swift in Sources */,

View File

@ -20,20 +20,34 @@ public class RxScrollViewDelegateProxy
, UIScrollViewDelegate
, DelegateProxyType {
fileprivate var _contentOffsetSubject: ReplaySubject<CGPoint>?
fileprivate var _contentOffsetBehaviorSubject: BehaviorSubject<CGPoint>?
fileprivate var _contentOffsetPublishSubject: PublishSubject<()>?
/// Typed parent object.
public weak fileprivate(set) var scrollView: UIScrollView?
/// Optimized version used for observing content offset changes.
internal var contentOffsetSubject: Observable<CGPoint> {
if _contentOffsetSubject == nil {
let replaySubject = ReplaySubject<CGPoint>.create(bufferSize:1)
_contentOffsetSubject = replaySubject
replaySubject.on(.next(self.scrollView?.contentOffset ?? CGPoint.zero))
internal var contentOffsetBehaviorSubject: BehaviorSubject<CGPoint> {
if let subject = _contentOffsetBehaviorSubject {
return subject
}
return _contentOffsetSubject!
let subject = BehaviorSubject<CGPoint>(value: self.scrollView?.contentOffset ?? CGPoint.zero)
_contentOffsetBehaviorSubject = subject
return subject
}
/// Optimized version used for observing content offset changes.
internal var contentOffsetPublishSubject: PublishSubject<()> {
if let subject = _contentOffsetPublishSubject {
return subject
}
let subject = PublishSubject<()>()
_contentOffsetPublishSubject = subject
return subject
}
/// Initializes `RxScrollViewDelegateProxy`
@ -48,8 +62,11 @@ public class RxScrollViewDelegateProxy
/// For more information take a look at `DelegateProxyType`.
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if let contentOffset = _contentOffsetSubject {
contentOffset.on(.next(scrollView.contentOffset))
if let subject = _contentOffsetBehaviorSubject {
subject.on(.next(scrollView.contentOffset))
}
if let subject = _contentOffsetPublishSubject {
subject.on(.next())
}
self._forwardToDelegate?.scrollViewDidScroll?(scrollView)
}
@ -76,8 +93,12 @@ public class RxScrollViewDelegateProxy
}
deinit {
if let contentOffset = _contentOffsetSubject {
contentOffset.on(.completed)
if let subject = _contentOffsetBehaviorSubject {
subject.on(.completed)
}
if let subject = _contentOffsetPublishSubject {
subject.on(.completed)
}
}
}

View File

@ -0,0 +1,40 @@
//
// RxWebViewDelegateProxy.swift
// RxCocoa
//
// Created by Andrew Breckenridge on 9/26/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import UIKit
#if !RX_NO_MODULE
import RxSwift
#endif
public class RxWebViewDelegateProxy
: DelegateProxy
, DelegateProxyType
, UIWebViewDelegate {
/**
For more information take a look at `DelegateProxyType`.
*/
public class func setCurrentDelegate(_ delegate: AnyObject?, toObject object: AnyObject) {
let webView: UIWebView = castOrFatalError(object)
webView.delegate = castOptionalOrFatalError(delegate)
}
/**
For more information take a look at `DelegateProxyType`.
*/
public class func currentDelegateFor(_ object: AnyObject) -> AnyObject? {
let webView: UIWebView = castOrFatalError(object)
return webView.delegate
}
}
#endif

View File

@ -6,8 +6,6 @@
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
#if os(iOS) || os(tvOS)
import Foundation

View File

@ -23,6 +23,15 @@ extension Reactive where Base: UIBarButtonItem {
UIElement.isEnabled = value
}
}
/**
Bindable sink for `title` property.
*/
public var title: UIBindingObserver<Base, String> {
return UIBindingObserver(UIElement: self.base) { UIElement, value in
UIElement.title = value
}
}
/// Reactive wrapper for target action pattern on `self`.
public var tap: ControlEvent<Void> {

View File

@ -42,7 +42,7 @@ extension Reactive where Base: UIScrollView {
scrollView.contentOffset = contentOffset
}
return ControlProperty(values: proxy.contentOffsetSubject, valueSink: bindingObserver)
return ControlProperty(values: proxy.contentOffsetBehaviorSubject, valueSink: bindingObserver)
}
/// Bindable sink for `scrollEnabled` property.
@ -54,7 +54,7 @@ extension Reactive where Base: UIScrollView {
/// Reactive wrapper for delegate method `scrollViewDidScroll`
public var didScroll: ControlEvent<Void> {
let source = RxScrollViewDelegateProxy.proxyForObject(base).contentOffsetSubject.map { _ in () }
let source = RxScrollViewDelegateProxy.proxyForObject(base).contentOffsetPublishSubject
return ControlEvent(events: source)
}

View File

@ -0,0 +1,55 @@
//
// UIWebView+Rx.swift
// RxCocoa
//
// Created by Andrew Breckenridge on 8/30/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import Foundation
import UIKit
#if !RX_NO_MODULE
import RxSwift
#endif
extension Reactive where Base: UIWebView {
/**
Reactive wrapper for `delegate`.
For more information take a look at `DelegateProxyType` protocol documentation.
*/
public var delegate: DelegateProxy {
return RxWebViewDelegateProxy.proxyForObject(base)
}
/**
Reactive wrapper for `delegate` message.
*/
public var didStartLoad: Observable<Void> {
return delegate
.methodInvoked(#selector(UIWebViewDelegate.webViewDidStartLoad(_:)))
.map {_ in}
}
/**
Reactive wrapper for `delegate` message.
*/
public var didFinishLoad: Observable<Void> {
return delegate
.methodInvoked(#selector(UIWebViewDelegate.webViewDidFinishLoad(_:)))
.map {_ in}
}
/**
Reactive wrapper for `delegate` message.
*/
public var didFailLoad: Observable<Error> {
return delegate
.methodInvoked(#selector(UIWebViewDelegate.webView(_:didFailLoadWithError:)))
.map { a in
return try castOrThrow(Error.self, a[1])
}
}
}
#endif

View File

@ -1,6 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11542" systemVersion="16B2555" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="ew7-Ty-fzC">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11524"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -14,53 +18,9 @@
<viewControllerLayoutGuide type="bottom" id="Zb9-9p-7u0"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="oOI-v8-i8g">
<rect key="frame" x="0.0" y="64" width="375" height="667"/>
<rect key="frame" x="0.0" y="64" width="375" height="603"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7Wy-Xx-ged">
<rect key="frame" x="0.0" y="0.0" width="375" height="603"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Geolocation is not enabled for this app" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QDz-hL-6ve">
<rect key="frame" x="47.5" y="168" width="280" height="67.5"/>
<constraints>
<constraint firstAttribute="width" constant="280" id="OAO-CK-RYp"/>
</constraints>
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle1"/>
<color key="textColor" red="0.94901960780000005" green="0.32549019610000002" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Please enable it in the app preferences" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cbw-8G-pl6">
<rect key="frame" x="47.5" y="243.5" width="280" height="42.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="q8m-1B-H0o">
<rect key="frame" x="87.5" y="316" width="200" height="28"/>
<color key="backgroundColor" red="0.17483745805369111" green="0.60840499161073824" blue="0.84705882349999995" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="200" id="qDe-NM-H36"/>
</constraints>
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="13"/>
<state key="normal" title="OPEN APP PREFERENCES"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="3"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="q8m-1B-H0o" firstAttribute="top" secondItem="cbw-8G-pl6" secondAttribute="bottom" constant="30" id="8Pd-KG-yad"/>
<constraint firstItem="QDz-hL-6ve" firstAttribute="centerY" secondItem="7Wy-Xx-ged" secondAttribute="centerY" constant="-100" id="CP1-Ta-e39"/>
<constraint firstItem="cbw-8G-pl6" firstAttribute="width" secondItem="QDz-hL-6ve" secondAttribute="width" id="OXx-2G-Yaz"/>
<constraint firstItem="q8m-1B-H0o" firstAttribute="centerX" secondItem="7Wy-Xx-ged" secondAttribute="centerX" id="Qni-Ne-BEo"/>
<constraint firstItem="cbw-8G-pl6" firstAttribute="centerX" secondItem="7Wy-Xx-ged" secondAttribute="centerX" id="Thp-mo-mXr"/>
<constraint firstItem="cbw-8G-pl6" firstAttribute="top" secondItem="QDz-hL-6ve" secondAttribute="bottom" constant="8" id="ehi-8n-5Gy"/>
<constraint firstItem="QDz-hL-6ve" firstAttribute="centerX" secondItem="7Wy-Xx-ged" secondAttribute="centerX" id="iQe-C8-u67"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JL2-pm-xcV">
<rect key="frame" x="0.0" y="0.0" width="375" height="603"/>
<subviews>
@ -122,14 +82,10 @@
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="7Wy-Xx-ged" firstAttribute="top" secondItem="oOI-v8-i8g" secondAttribute="top" id="0j8-qY-okH"/>
<constraint firstItem="7Wy-Xx-ged" firstAttribute="leading" secondItem="oOI-v8-i8g" secondAttribute="leading" id="5KT-7g-gmw"/>
<constraint firstItem="JL2-pm-xcV" firstAttribute="leading" secondItem="oOI-v8-i8g" secondAttribute="leading" id="Al3-6E-7TX"/>
<constraint firstAttribute="trailing" secondItem="JL2-pm-xcV" secondAttribute="trailing" id="TJu-9q-C3r"/>
<constraint firstItem="JL2-pm-xcV" firstAttribute="top" secondItem="ouX-MF-szB" secondAttribute="bottom" id="UH9-TU-sn1"/>
<constraint firstItem="Zb9-9p-7u0" firstAttribute="top" secondItem="7Wy-Xx-ged" secondAttribute="bottom" id="X8x-Ij-bEV"/>
<constraint firstItem="Zb9-9p-7u0" firstAttribute="top" secondItem="JL2-pm-xcV" secondAttribute="bottom" id="eBw-6B-iSn"/>
<constraint firstAttribute="trailing" secondItem="7Wy-Xx-ged" secondAttribute="trailing" id="jPm-IL-4ry"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="nwM-mR-Dgg"/>
@ -139,10 +95,54 @@
<outlet property="button2" destination="3q9-a2-ojh" id="kww-B7-jeL"/>
<outlet property="label" destination="Edb-tp-cly" id="Icm-cQ-9QB"/>
<outlet property="noGeolocationView" destination="7Wy-Xx-ged" id="tce-XP-hMK"/>
<outlet property="view" destination="oOI-v8-i8g" id="g8m-Rt-ZGi"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="yOr-7R-tPd" userLabel="First Responder" sceneMemberID="firstResponder"/>
<view contentMode="scaleToFill" id="7Wy-Xx-ged">
<rect key="frame" x="0.0" y="0.0" width="375" height="603"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Geolocation is not enabled for this app" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QDz-hL-6ve">
<rect key="frame" x="48" y="168.5" width="280" height="67.5"/>
<constraints>
<constraint firstAttribute="width" constant="280" id="OAO-CK-RYp"/>
</constraints>
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle1"/>
<color key="textColor" red="0.94901960780000005" green="0.32549019610000002" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Please enable it in the app preferences" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cbw-8G-pl6">
<rect key="frame" x="48" y="244" width="280" height="43"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="q8m-1B-H0o">
<rect key="frame" x="88" y="317" width="200" height="28"/>
<color key="backgroundColor" red="0.17483745805369111" green="0.60840499161073824" blue="0.84705882349999995" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="200" id="qDe-NM-H36"/>
</constraints>
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="13"/>
<state key="normal" title="OPEN APP PREFERENCES"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="3"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="q8m-1B-H0o" firstAttribute="top" secondItem="cbw-8G-pl6" secondAttribute="bottom" constant="30" id="8Pd-KG-yad"/>
<constraint firstItem="QDz-hL-6ve" firstAttribute="centerY" secondItem="7Wy-Xx-ged" secondAttribute="centerY" constant="-100" id="CP1-Ta-e39"/>
<constraint firstItem="cbw-8G-pl6" firstAttribute="width" secondItem="QDz-hL-6ve" secondAttribute="width" id="OXx-2G-Yaz"/>
<constraint firstItem="q8m-1B-H0o" firstAttribute="centerX" secondItem="7Wy-Xx-ged" secondAttribute="centerX" id="Qni-Ne-BEo"/>
<constraint firstItem="cbw-8G-pl6" firstAttribute="centerX" secondItem="7Wy-Xx-ged" secondAttribute="centerX" id="Thp-mo-mXr"/>
<constraint firstItem="cbw-8G-pl6" firstAttribute="top" secondItem="QDz-hL-6ve" secondAttribute="bottom" constant="8" id="ehi-8n-5Gy"/>
<constraint firstItem="QDz-hL-6ve" firstAttribute="centerX" secondItem="7Wy-Xx-ged" secondAttribute="centerX" id="iQe-C8-u67"/>
</constraints>
</view>
</objects>
<point key="canvasLocation" x="805" y="341"/>
</scene>

View File

@ -14,48 +14,35 @@ import CoreLocation
#endif
private extension Reactive where Base: UILabel {
var driveCoordinates: UIBindingObserver<Base, CLLocationCoordinate2D> {
var coordinates: UIBindingObserver<Base, CLLocationCoordinate2D> {
return UIBindingObserver(UIElement: base) { label, location in
label.text = "Lat: \(location.latitude)\nLon: \(location.longitude)"
}
}
}
private extension Reactive where Base: UIView {
var driveAuthorization: UIBindingObserver<Base, Bool> {
return UIBindingObserver(UIElement: base) { view, authorized in
if authorized {
view.isHidden = true
view.superview?.sendSubview(toBack:view)
}
else {
view.isHidden = false
view.superview?.bringSubview(toFront:view)
}
}
}
}
class GeolocationViewController: ViewController {
@IBOutlet weak private var noGeolocationView: UIView!
@IBOutlet weak private var button: UIButton!
@IBOutlet weak private var button2: UIButton!
@IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(noGeolocationView)
let geolocationService = GeolocationService.instance
geolocationService.authorized
.drive(noGeolocationView.rx.driveAuthorization)
.drive(noGeolocationView.rx.isHidden)
.addDisposableTo(disposeBag)
geolocationService.location
.drive(label.rx.driveCoordinates)
.drive(label.rx.coordinates)
.addDisposableTo(disposeBag)
button.rx.tap
.bindNext { [weak self] in
self?.openAppPreferences()
@ -72,5 +59,5 @@ class GeolocationViewController: ViewController {
private func openAppPreferences() {
UIApplication.shared.openURL(URL(string: UIApplicationOpenSettingsURLString)!)
}
}

View File

@ -0,0 +1 @@
../../RxCocoa/iOS/Proxies/RxWebViewDelegateProxy.swift

View File

@ -0,0 +1 @@
../../RxCocoa/iOS/UIWebView+Rx.swift

View File

@ -62,6 +62,7 @@ extension RxSearchControllerDelegateProxy: TestDelegateProtocol {
}
extension RxPickerViewDelegateProxy: TestDelegateProtocol {
}
extension RxWebViewDelegateProxy: TestDelegateProtocol {}
#endif
// MARK: Tests
@ -133,6 +134,16 @@ extension DelegateProxyTest {
}
}
#endif
// MARK: UIWebView
#if os(iOS)
extension DelegateProxyTest {
func test_UIWebViewDelegateExtension() {
performDelegateTest(UIWebViewSubclass(frame: CGRect.zero))
}
}
#endif
// MARK: Mocks
class ExtendTableViewDelegateProxy
@ -467,5 +478,30 @@ class UIPickerViewSubclass
onProxyForObject: self)
}
}
class UIWebViewSubclass: UIWebView, TestDelegateControl {
func doThatTest(_ value: Int) {
(delegate as! TestDelegateProtocol).testEventHappened?(value)
}
var testSentMessage: Observable<Int> {
return rx.delegate
.sentMessage(#selector(TestDelegateProtocol.testEventHappened(_:)))
.map { a in (a[0] as! NSNumber).intValue }
}
var testMethodInvoked: Observable<Int> {
return rx.delegate
.methodInvoked(#selector(TestDelegateProtocol.testEventHappened(_:)))
.map { a in (a[0] as! NSNumber).intValue }
}
func setMineForwardDelegate(_ testDelegate: TestDelegateProtocol) -> Disposable {
return RxWebViewDelegateProxy.installForwardDelegate(testDelegate,
retainDelegate: false,
onProxyForObject: self)
}
}
#endif

View File

@ -21,4 +21,13 @@ extension UIBarButtonItemTests {
func testBarButtonItem_DelegateEventCompletesOnDealloc() {
ensureEventDeallocated({ UIBarButtonItem() }) { (view: UIBarButtonItem) in view.rx.tap }
}
func testButton_titleObserver() {
let button = UIBarButtonItem()
XCTAssertEqual(button.title, nil)
let text = "title"
_ = Observable.just(text).bindTo(button.rx.title)
XCTAssertEqual(button.title, text)
}
}

View File

@ -41,17 +41,52 @@ extension UIScrollViewTests {
}
func testScrollViewDidScroll() {
let scrollView = UIScrollView()
var didScroll = false
var completed = false
autoreleasepool {
let scrollView = UIScrollView()
var didScroll = false
let subscription = scrollView.rx.didScroll.subscribe(onNext: {
didScroll = true
})
_ = scrollView.rx.didScroll.subscribe(onNext: {
didScroll = true
}, onCompleted: {
completed = true
})
scrollView.delegate!.scrollViewDidScroll!(scrollView)
XCTAssertFalse(didScroll)
XCTAssertTrue(didScroll)
subscription.dispose()
scrollView.delegate!.scrollViewDidScroll!(scrollView)
XCTAssertTrue(didScroll)
}
XCTAssertTrue(completed)
}
func testScrollViewContentOffset() {
var completed = false
autoreleasepool {
let scrollView = UIScrollView()
scrollView.contentOffset = .zero
var contentOffset = CGPoint(x: -1, y: -1)
_ = scrollView.rx.contentOffset.subscribe(onNext: { value in
contentOffset = value
}, onCompleted: {
completed = true
})
XCTAssertEqual(contentOffset, .zero)
scrollView.contentOffset = CGPoint(x: 2, y: 2)
scrollView.delegate!.scrollViewDidScroll!(scrollView)
XCTAssertEqual(contentOffset, CGPoint(x: 2, y: 2))
}
XCTAssertTrue(completed)
}
func testScrollViewDidZoom() {
@ -62,6 +97,8 @@ extension UIScrollViewTests {
didZoom = true
})
XCTAssertFalse(didZoom)
scrollView.delegate!.scrollViewDidZoom!(scrollView)
XCTAssertTrue(didZoom)
@ -76,6 +113,8 @@ extension UIScrollViewTests {
didScrollToTop = true
})
XCTAssertFalse(didScrollToTop)
scrollView.delegate!.scrollViewDidScrollToTop!(scrollView)
XCTAssertTrue(didScrollToTop)

View File

@ -0,0 +1,68 @@
//
// UIWebView+RxTests.swift
// Tests
//
// Created by Andrew Breckenridge on 8/30/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import Foundation
import UIKit
import RxSwift
import RxCocoa
import RxBlocking
import XCTest
class UIWebViewTests: RxTest {}
fileprivate let testHTMLString = "<html><head></head><body><h1>🔥</h1></body></html>"
extension UIWebViewTests {
func testDidStartLoad() {
let webView = UIWebView()
var didStartLoad = false
let subscription = webView.rx.didStartLoad.subscribe(onNext: {
didStartLoad = true
})
webView.delegate!.webViewDidStartLoad!(webView)
XCTAssertTrue(didStartLoad)
subscription.dispose()
}
func testDidFinishLoad() {
let webView = UIWebView()
var didFinishLoad = false
let subscription = webView.rx.didFinishLoad.subscribe(onNext: {
didFinishLoad = true
})
webView.delegate!.webViewDidFinishLoad!(webView)
XCTAssertTrue(didFinishLoad)
subscription.dispose()
}
func testDidFailLoad() {
let webView = UIWebView()
var didFailLoad = false
let subscription = webView.rx.didFailLoad.subscribe { _ in
didFailLoad = true
}
webView.delegate!.webView!(webView, didFailLoadWithError: NSError(domain: "", code: 0, userInfo: .none))
XCTAssertTrue(didFailLoad)
subscription.dispose()
}
}
#endif