This commit is contained in:
DDIsFriend
2023-08-18 17:28:57 +08:00
commit f0e8a1709d
4282 changed files with 192396 additions and 0 deletions

9
Pods/RxCocoa/LICENSE.md generated Normal file
View File

@@ -0,0 +1,9 @@
**The MIT License**
**Copyright © 2015 Krunoslav Zaher, Shai Mishali**
**All rights reserved.**
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,181 @@
//
// Bag.swift
// Platform
//
// Created by Krunoslav Zaher on 2/28/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Swift
let arrayDictionaryMaxSize = 30
struct BagKey {
/**
Unique identifier for object added to `Bag`.
It's underlying type is UInt64. If we assume there in an idealized CPU that works at 4GHz,
it would take ~150 years of continuous running time for it to overflow.
*/
fileprivate let rawValue: UInt64
}
/**
Data structure that represents a bag of elements typed `T`.
Single element can be stored multiple times.
Time and space complexity of insertion and deletion is O(n).
It is suitable for storing small number of elements.
*/
struct Bag<T> : CustomDebugStringConvertible {
/// Type of identifier for inserted elements.
typealias KeyType = BagKey
typealias Entry = (key: BagKey, value: T)
private var _nextKey: BagKey = BagKey(rawValue: 0)
// data
// first fill inline variables
var _key0: BagKey?
var _value0: T?
// then fill "array dictionary"
var _pairs = ContiguousArray<Entry>()
// last is sparse dictionary
var _dictionary: [BagKey: T]?
var _onlyFastPath = true
/// Creates new empty `Bag`.
init() {
}
/**
Inserts `value` into bag.
- parameter element: Element to insert.
- returns: Key that can be used to remove element from bag.
*/
mutating func insert(_ element: T) -> BagKey {
let key = _nextKey
_nextKey = BagKey(rawValue: _nextKey.rawValue &+ 1)
if _key0 == nil {
_key0 = key
_value0 = element
return key
}
_onlyFastPath = false
if _dictionary != nil {
_dictionary![key] = element
return key
}
if _pairs.count < arrayDictionaryMaxSize {
_pairs.append((key: key, value: element))
return key
}
_dictionary = [key: element]
return key
}
/// - returns: Number of elements in bag.
var count: Int {
let dictionaryCount: Int = _dictionary?.count ?? 0
return (_value0 != nil ? 1 : 0) + _pairs.count + dictionaryCount
}
/// Removes all elements from bag and clears capacity.
mutating func removeAll() {
_key0 = nil
_value0 = nil
_pairs.removeAll(keepingCapacity: false)
_dictionary?.removeAll(keepingCapacity: false)
}
/**
Removes element with a specific `key` from bag.
- parameter key: Key that identifies element to remove from bag.
- returns: Element that bag contained, or nil in case element was already removed.
*/
mutating func removeKey(_ key: BagKey) -> T? {
if _key0 == key {
_key0 = nil
let value = _value0!
_value0 = nil
return value
}
if let existingObject = _dictionary?.removeValue(forKey: key) {
return existingObject
}
for i in 0 ..< _pairs.count where _pairs[i].key == key {
let value = _pairs[i].value
_pairs.remove(at: i)
return value
}
return nil
}
}
extension Bag {
/// A textual representation of `self`, suitable for debugging.
var debugDescription : String {
"\(self.count) elements in Bag"
}
}
extension Bag {
/// Enumerates elements inside the bag.
///
/// - parameter action: Enumeration closure.
func forEach(_ action: (T) -> Void) {
if _onlyFastPath {
if let value0 = _value0 {
action(value0)
}
return
}
let value0 = _value0
let dictionary = _dictionary
if let value0 = value0 {
action(value0)
}
for i in 0 ..< _pairs.count {
action(_pairs[i].value)
}
if dictionary?.count ?? 0 > 0 {
for element in dictionary!.values {
action(element)
}
}
}
}
extension BagKey: Hashable {
func hash(into hasher: inout Hasher) {
hasher.combine(rawValue)
}
}
func ==(lhs: BagKey, rhs: BagKey) -> Bool {
lhs.rawValue == rhs.rawValue
}

View File

@@ -0,0 +1,23 @@
//
// InfiniteSequence.swift
// Platform
//
// Created by Krunoslav Zaher on 6/13/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
/// Sequence that repeats `repeatedValue` infinite number of times.
struct InfiniteSequence<Element> : Sequence {
typealias Iterator = AnyIterator<Element>
private let repeatedValue: Element
init(repeatedValue: Element) {
self.repeatedValue = repeatedValue
}
func makeIterator() -> Iterator {
let repeatedValue = self.repeatedValue
return AnyIterator { repeatedValue }
}
}

View File

@@ -0,0 +1,111 @@
//
// PriorityQueue.swift
// Platform
//
// Created by Krunoslav Zaher on 12/27/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
struct PriorityQueue<Element> {
private let hasHigherPriority: (Element, Element) -> Bool
private let isEqual: (Element, Element) -> Bool
private var elements = [Element]()
init(hasHigherPriority: @escaping (Element, Element) -> Bool, isEqual: @escaping (Element, Element) -> Bool) {
self.hasHigherPriority = hasHigherPriority
self.isEqual = isEqual
}
mutating func enqueue(_ element: Element) {
elements.append(element)
bubbleToHigherPriority(elements.count - 1)
}
func peek() -> Element? {
elements.first
}
var isEmpty: Bool {
elements.count == 0
}
mutating func dequeue() -> Element? {
guard let front = peek() else {
return nil
}
removeAt(0)
return front
}
mutating func remove(_ element: Element) {
for i in 0 ..< elements.count {
if self.isEqual(elements[i], element) {
removeAt(i)
return
}
}
}
private mutating func removeAt(_ index: Int) {
let removingLast = index == elements.count - 1
if !removingLast {
elements.swapAt(index, elements.count - 1)
}
_ = elements.popLast()
if !removingLast {
bubbleToHigherPriority(index)
bubbleToLowerPriority(index)
}
}
private mutating func bubbleToHigherPriority(_ initialUnbalancedIndex: Int) {
precondition(initialUnbalancedIndex >= 0)
precondition(initialUnbalancedIndex < elements.count)
var unbalancedIndex = initialUnbalancedIndex
while unbalancedIndex > 0 {
let parentIndex = (unbalancedIndex - 1) / 2
guard self.hasHigherPriority(elements[unbalancedIndex], elements[parentIndex]) else { break }
elements.swapAt(unbalancedIndex, parentIndex)
unbalancedIndex = parentIndex
}
}
private mutating func bubbleToLowerPriority(_ initialUnbalancedIndex: Int) {
precondition(initialUnbalancedIndex >= 0)
precondition(initialUnbalancedIndex < elements.count)
var unbalancedIndex = initialUnbalancedIndex
while true {
let leftChildIndex = unbalancedIndex * 2 + 1
let rightChildIndex = unbalancedIndex * 2 + 2
var highestPriorityIndex = unbalancedIndex
if leftChildIndex < elements.count && self.hasHigherPriority(elements[leftChildIndex], elements[highestPriorityIndex]) {
highestPriorityIndex = leftChildIndex
}
if rightChildIndex < elements.count && self.hasHigherPriority(elements[rightChildIndex], elements[highestPriorityIndex]) {
highestPriorityIndex = rightChildIndex
}
guard highestPriorityIndex != unbalancedIndex else { break }
elements.swapAt(highestPriorityIndex, unbalancedIndex)
unbalancedIndex = highestPriorityIndex
}
}
}
extension PriorityQueue : CustomDebugStringConvertible {
var debugDescription: String {
elements.debugDescription
}
}

View File

@@ -0,0 +1,148 @@
//
// Queue.swift
// Platform
//
// Created by Krunoslav Zaher on 3/21/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
/**
Data structure that represents queue.
Complexity of `enqueue`, `dequeue` is O(1) when number of operations is
averaged over N operations.
Complexity of `peek` is O(1).
*/
struct Queue<T>: Sequence {
/// Type of generator.
typealias Generator = AnyIterator<T>
private let resizeFactor = 2
private var storage: ContiguousArray<T?>
private var innerCount = 0
private var pushNextIndex = 0
private let initialCapacity: Int
/**
Creates new queue.
- parameter capacity: Capacity of newly created queue.
*/
init(capacity: Int) {
initialCapacity = capacity
storage = ContiguousArray<T?>(repeating: nil, count: capacity)
}
private var dequeueIndex: Int {
let index = pushNextIndex - count
return index < 0 ? index + storage.count : index
}
/// - returns: Is queue empty.
var isEmpty: Bool { count == 0 }
/// - returns: Number of elements inside queue.
var count: Int { innerCount }
/// - returns: Element in front of a list of elements to `dequeue`.
func peek() -> T {
precondition(count > 0)
return storage[dequeueIndex]!
}
mutating private func resizeTo(_ size: Int) {
var newStorage = ContiguousArray<T?>(repeating: nil, count: size)
let count = self.count
let dequeueIndex = self.dequeueIndex
let spaceToEndOfQueue = storage.count - dequeueIndex
// first batch is from dequeue index to end of array
let countElementsInFirstBatch = Swift.min(count, spaceToEndOfQueue)
// second batch is wrapped from start of array to end of queue
let numberOfElementsInSecondBatch = count - countElementsInFirstBatch
newStorage[0 ..< countElementsInFirstBatch] = storage[dequeueIndex ..< (dequeueIndex + countElementsInFirstBatch)]
newStorage[countElementsInFirstBatch ..< (countElementsInFirstBatch + numberOfElementsInSecondBatch)] = storage[0 ..< numberOfElementsInSecondBatch]
self.innerCount = count
pushNextIndex = count
storage = newStorage
}
/// Enqueues `element`.
///
/// - parameter element: Element to enqueue.
mutating func enqueue(_ element: T) {
if count == storage.count {
resizeTo(Swift.max(storage.count, 1) * resizeFactor)
}
storage[pushNextIndex] = element
pushNextIndex += 1
innerCount += 1
if pushNextIndex >= storage.count {
pushNextIndex -= storage.count
}
}
private mutating func dequeueElementOnly() -> T {
precondition(count > 0)
let index = dequeueIndex
defer {
storage[index] = nil
innerCount -= 1
}
return storage[index]!
}
/// Dequeues element or throws an exception in case queue is empty.
///
/// - returns: Dequeued element.
mutating func dequeue() -> T? {
if self.count == 0 {
return nil
}
defer {
let downsizeLimit = storage.count / (resizeFactor * resizeFactor)
if count < downsizeLimit && downsizeLimit >= initialCapacity {
resizeTo(storage.count / resizeFactor)
}
}
return dequeueElementOnly()
}
/// - returns: Generator of contained elements.
func makeIterator() -> AnyIterator<T> {
var i = dequeueIndex
var innerCount = count
return AnyIterator {
if innerCount == 0 {
return nil
}
defer {
innerCount -= 1
i += 1
}
if i >= self.storage.count {
i -= self.storage.count
}
return self.storage[i]
}
}
}

View File

@@ -0,0 +1,21 @@
//
// DispatchQueue+Extensions.swift
// Platform
//
// Created by Krunoslav Zaher on 10/22/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Dispatch
extension DispatchQueue {
private static var token: DispatchSpecificKey<()> = {
let key = DispatchSpecificKey<()>()
DispatchQueue.main.setSpecific(key: key, value: ())
return key
}()
static var isMain: Bool {
DispatchQueue.getSpecific(key: token) != nil
}
}

View File

@@ -0,0 +1,35 @@
//
// Platform.Darwin.swift
// Platform
//
// Created by Krunoslav Zaher on 12/29/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
import Darwin
import Foundation
extension Thread {
static func setThreadLocalStorageValue<T: AnyObject>(_ value: T?, forKey key: NSCopying) {
let currentThread = Thread.current
let threadDictionary = currentThread.threadDictionary
if let newValue = value {
threadDictionary[key] = newValue
}
else {
threadDictionary[key] = nil
}
}
static func getThreadLocalStorageValueForKey<T>(_ key: NSCopying) -> T? {
let currentThread = Thread.current
let threadDictionary = currentThread.threadDictionary
return threadDictionary[key] as? T
}
}
#endif

View File

@@ -0,0 +1,32 @@
//
// Platform.Linux.swift
// Platform
//
// Created by Krunoslav Zaher on 12/29/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(Linux)
import Foundation
extension Thread {
static func setThreadLocalStorageValue<T: AnyObject>(_ value: T?, forKey key: String) {
if let newValue = value {
Thread.current.threadDictionary[key] = newValue
}
else {
Thread.current.threadDictionary[key] = nil
}
}
static func getThreadLocalStorageValueForKey<T: AnyObject>(_ key: String) -> T? {
let currentThread = Thread.current
let threadDictionary = currentThread.threadDictionary
return threadDictionary[key] as? T
}
}
#endif

View File

@@ -0,0 +1,34 @@
//
// RecursiveLock.swift
// Platform
//
// Created by Krunoslav Zaher on 12/18/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
#if TRACE_RESOURCES
class RecursiveLock: NSRecursiveLock {
override init() {
_ = Resources.incrementTotal()
super.init()
}
override func lock() {
super.lock()
_ = Resources.incrementTotal()
}
override func unlock() {
super.unlock()
_ = Resources.decrementTotal()
}
deinit {
_ = Resources.decrementTotal()
}
}
#else
typealias RecursiveLock = NSRecursiveLock
#endif

254
Pods/RxCocoa/README.md generated Normal file
View File

@@ -0,0 +1,254 @@
<p align="center">
<img src="assets/RxSwift_Logo.png" width="35%" alt="RxSwift Logo" />
<br />
<a href="https://actions-badge.atrox.dev/ReactiveX/RxSwift/goto" target="_blank"><img src="https://github.com/ReactiveX/RxSwift/workflows/RxSwift/badge.svg?branch=main" alt="Build Status" /></a>
<img src="https://img.shields.io/badge/platforms-iOS%20%7C%20macOS%20%7C%20tvOS%20%7C%20watchOS%20%7C%20Linux-333333.svg" alt="Supported Platforms: iOS, macOS, tvOS, watchOS & Linux" />
<br />
<a href="https://cocoapods.org/pods/RxSwift" alt="RxSwift on CocoaPods" title="RxSwift on CocoaPods"><img src="https://img.shields.io/cocoapods/v/RxSwift.svg" /></a>
<a href="https://github.com/Carthage/Carthage" alt="RxSwift on Carthage" title="RxSwift on Carthage"><img src="https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat" /></a>
<a href="https://github.com/apple/swift-package-manager" alt="RxSwift on Swift Package Manager" title="RxSwift on Swift Package Manager"><img src="https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg" /></a>
</p>
Rx is a [generic abstraction of computation](https://youtu.be/looJcaeboBY) expressed through `Observable<Element>` interface, which lets you broadcast and subscribe to values and other events from an `Observable` stream.
RxSwift is the Swift-specific implementation of the [Reactive Extensions](http://reactivex.io) standard.
<p align="center"><img src="assets/example.png" width="55%" alt="RxSwift Observable Example of a price constantly changing and updating the app's UI" /></p>
While this version aims to stay true to the original spirit and naming conventions of Rx, this projects also aims to provide a true Swift-first API for Rx APIs.
Cross platform documentation can be found on [ReactiveX.io](http://reactivex.io/).
Like other Rx implementation, RxSwift's intention is to enable easy composition of asynchronous operations and streams of data in the form of `Observable` objects and a suite of methods to transform and compose these pieces of asynchronous work.
KVO observation, async operations, UI Events and other streams of data are all unified under [abstraction of sequence](Documentation/GettingStarted.md#observables-aka-sequences). This is the reason why Rx is so simple, elegant and powerful.
## I came here because I want to ...
###### ... understand
* [why use rx?](Documentation/Why.md)
* [the basics, getting started with RxSwift](Documentation/GettingStarted.md)
* [traits](Documentation/Traits.md) - what are `Single`, `Completable`, `Maybe`, `Driver`, and `ControlProperty` ... and why do they exist?
* [testing](Documentation/UnitTests.md)
* [tips and common errors](Documentation/Tips.md)
* [debugging](Documentation/GettingStarted.md#debugging)
* [the math behind Rx](Documentation/MathBehindRx.md)
* [what are hot and cold observable sequences?](Documentation/HotAndColdObservables.md)
###### ... install
* Integrate RxSwift/RxCocoa with my app. [Installation Guide](#installation)
###### ... hack around
* with the example app. [Running Example App](Documentation/ExampleApp.md)
* with operators in playgrounds. [Playgrounds](Documentation/Playgrounds.md)
###### ... interact
* All of this is great, but it would be nice to talk with other people using RxSwift and exchange experiences. <br />[Join Slack Channel](http://slack.rxswift.org)
* Report a problem using the library. [Open an Issue With Bug Template](.github/ISSUE_TEMPLATE.md)
* Request a new feature. [Open an Issue With Feature Request Template](Documentation/NewFeatureRequestTemplate.md)
* Help out [Check out contribution guide](CONTRIBUTING.md)
###### ... compare
* [with Combine and ReactiveSwift](Documentation/ComparisonWithOtherLibraries.md).
###### ... understand the structure
RxSwift is as compositional as the asynchronous work it drives. The core unit is RxSwift itself, while other dependencies can be added for UI Work, testing, and more.
It comprises five separate components depending on each other in the following way:
```none
┌──────────────┐ ┌──────────────┐
│ RxCocoa ├────▶ RxRelay │
└───────┬──────┘ └──────┬───────┘
│ │
┌───────▼──────────────────▼───────┐
│ RxSwift │
└───────▲──────────────────▲───────┘
│ │
┌───────┴──────┐ ┌──────┴───────┐
│ RxTest │ │ RxBlocking │
└──────────────┘ └──────────────┘
```
* **RxSwift**: The core of RxSwift, providing the Rx standard as (mostly) defined by [ReactiveX](https://reactivex.io). It has no other dependencies.
* **RxCocoa**: Provides Cocoa-specific capabilities for general iOS/macOS/watchOS & tvOS app development, such as Shared Sequences, Traits, and much more. It depends on both `RxSwift` and `RxRelay`.
* **RxRelay**: Provides `PublishRelay`, `BehaviorRelay` and `ReplayRelay`, three [simple wrappers around Subjects](https://github.com/ReactiveX/RxSwift/blob/main/Documentation/Subjects.md#relays). It depends on `RxSwift`.
* **RxTest** and **RxBlocking**: Provides testing capabilities for Rx-based systems. It depends on `RxSwift`.
## Usage
<table>
<tr>
<th width="30%">Here's an example</th>
<th width="30%">In Action</th>
</tr>
<tr>
<td>Define search for GitHub repositories ...</td>
<th rowspan="9"><img src="https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/GithubSearch.gif"></th>
</tr>
<tr>
<td><div class="highlight highlight-source-swift"><pre>
let searchResults = searchBar.rx.text.orEmpty
.throttle(.milliseconds(300), scheduler: MainScheduler.instance)
.distinctUntilChanged()
.flatMapLatest { query -> Observable&lt;[Repository]&gt; in
if query.isEmpty {
return .just([])
}
return searchGitHub(query)
.catchAndReturn([])
}
.observe(on: MainScheduler.instance)</pre></div></td>
</tr>
<tr>
<td>... then bind the results to your tableview</td>
</tr>
<tr>
<td width="30%"><div class="highlight highlight-source-swift"><pre>
searchResults
.bind(to: tableView.rx.items(cellIdentifier: "Cell")) {
(index, repository: Repository, cell) in
cell.textLabel?.text = repository.name
cell.detailTextLabel?.text = repository.url
}
.disposed(by: disposeBag)</pre></div></td>
</tr>
</table>
## Requirements
* Xcode 12.x
* Swift 5.x
For Xcode 11 and below, [use RxSwift 5.x](https://github.com/ReactiveX/RxSwift/releases/tag/5.1.1).
## Installation
RxSwift doesn't contain any external dependencies.
These are currently the supported installation options:
### Manual
Open Rx.xcworkspace, choose `RxExample` and hit run. This method will build everything and run the sample app
### [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html)
```ruby
# Podfile
use_frameworks!
target 'YOUR_TARGET_NAME' do
pod 'RxSwift', '6.5.0'
pod 'RxCocoa', '6.5.0'
end
# RxTest and RxBlocking make the most sense in the context of unit/integration tests
target 'YOUR_TESTING_TARGET' do
pod 'RxBlocking', '6.5.0'
pod 'RxTest', '6.5.0'
end
```
Replace `YOUR_TARGET_NAME` and then, in the `Podfile` directory, type:
```bash
$ pod install
```
### XCFrameworks
Each release starting with RxSwift 6 includes `*.xcframework` framework binaries.
Simply drag the needed framework binaries to your **Frameworks, Libraries, and Embedded Content** section under your target's **General** tab.
> **Note**: If you're using `RxCocoa`, be sure to also drag **RxCocoaRuntime.xcframework** before importing `RxCocoa`.
<img src="https://raw.githubusercontent.com/ReactiveX/RxSwift/main/assets/xcframeworks.png" alt="XCFrameworks instructions" width="65%">
### [Carthage](https://github.com/Carthage/Carthage)
Add this to `Cartfile`
```
github "ReactiveX/RxSwift" "6.5.0"
```
```bash
$ carthage update
```
#### Carthage as a Static Library
Carthage defaults to building RxSwift as a Dynamic Library.
If you wish to build RxSwift as a Static Library using Carthage you may use the script below to manually modify the framework type before building with Carthage:
```bash
carthage update RxSwift --platform iOS --no-build
sed -i -e 's/MACH_O_TYPE = mh_dylib/MACH_O_TYPE = staticlib/g' Carthage/Checkouts/RxSwift/Rx.xcodeproj/project.pbxproj
carthage build RxSwift --platform iOS
```
### [Swift Package Manager](https://github.com/apple/swift-package-manager)
> **Note**: There is a critical cross-dependency bug affecting many projects including RxSwift in Swift Package Manager. We've [filed a bug (SR-12303)](https://bugs.swift.org/browse/SR-12303) in early 2020 but have no answer yet. Your mileage may vary. A partial workaround can be found [here](https://github.com/ReactiveX/RxSwift/issues/2127#issuecomment-717830502).
Create a `Package.swift` file.
```swift
// swift-tools-version:5.0
import PackageDescription
let package = Package(
name: "RxTestProject",
dependencies: [
.package(url: "https://github.com/ReactiveX/RxSwift.git", .exact("6.5.0"))
],
targets: [
.target(name: "RxTestProject", dependencies: ["RxSwift", "RxCocoa"])
]
)
```
```bash
$ swift build
```
To build or test a module with RxTest dependency, set `TEST=1`.
```bash
$ TEST=1 swift test
```
### Manually using git submodules
* Add RxSwift as a submodule
```bash
$ git submodule add git@github.com:ReactiveX/RxSwift.git
```
* Drag `Rx.xcodeproj` into Project Navigator
* Go to `Project > Targets > Build Phases > Link Binary With Libraries`, click `+` and select `RxSwift`, `RxCocoa` and `RxRelay` targets
## References
* [http://reactivex.io/](http://reactivex.io/)
* [Reactive Extensions GitHub (GitHub)](https://github.com/Reactive-Extensions)
* [RxSwift RayWenderlich.com Book](https://store.raywenderlich.com/products/rxswift-reactive-programming-with-swift)
* [RxSwift: Debunking the myth of hard (YouTube)](https://www.youtube.com/watch?v=GdvLP0ZAhhc)
* [Boxue.io RxSwift Online Course](https://boxueio.com/series/rxswift-101) (Chinese 🇨🇳)
* [Expert to Expert: Brian Beckman and Erik Meijer - Inside the .NET Reactive Framework (Rx) (video)](https://youtu.be/looJcaeboBY)
* [Reactive Programming Overview (Jafar Husain from Netflix)](https://youtu.be/-8Y1-lE6NSA)
* [Subject/Observer is Dual to Iterator (paper)](http://csl.stanford.edu/~christos/pldi2010.fit/meijer.duality.pdf)
* [Rx standard sequence operators visualized (visualization tool)](http://rxmarbles.com/)
* [Haskell](https://www.haskell.org/)

View File

@@ -0,0 +1,88 @@
//
// ControlTarget.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 2/21/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS) || os(macOS)
import RxSwift
#if os(iOS) || os(tvOS)
import UIKit
typealias Control = UIKit.UIControl
#elseif os(macOS)
import Cocoa
typealias Control = Cocoa.NSControl
#endif
// This should be only used from `MainScheduler`
final class ControlTarget: RxTarget {
typealias Callback = (Control) -> Void
let selector: Selector = #selector(ControlTarget.eventHandler(_:))
weak var control: Control?
#if os(iOS) || os(tvOS)
let controlEvents: UIControl.Event
#endif
var callback: Callback?
#if os(iOS) || os(tvOS)
init(control: Control, controlEvents: UIControl.Event, callback: @escaping Callback) {
MainScheduler.ensureRunningOnMainThread()
self.control = control
self.controlEvents = controlEvents
self.callback = callback
super.init()
control.addTarget(self, action: selector, for: controlEvents)
let method = self.method(for: selector)
if method == nil {
rxFatalError("Can't find method")
}
}
#elseif os(macOS)
init(control: Control, callback: @escaping Callback) {
MainScheduler.ensureRunningOnMainThread()
self.control = control
self.callback = callback
super.init()
control.target = self
control.action = self.selector
let method = self.method(for: self.selector)
if method == nil {
rxFatalError("Can't find method")
}
}
#endif
@objc func eventHandler(_ sender: Control!) {
if let callback = self.callback, let control = self.control {
callback(control)
}
}
override func dispose() {
super.dispose()
#if os(iOS) || os(tvOS)
self.control?.removeTarget(self, action: self.selector, for: self.controlEvents)
#elseif os(macOS)
self.control?.target = nil
self.control?.action = nil
#endif
self.callback = nil
}
}
#endif

View File

@@ -0,0 +1,294 @@
//
// DelegateProxy.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 6/14/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if !os(Linux)
import RxSwift
#if SWIFT_PACKAGE && !os(Linux)
import RxCocoaRuntime
#endif
/// Base class for `DelegateProxyType` protocol.
///
/// This implementation is not thread safe and can be used only from one thread (Main thread).
open class DelegateProxy<P: AnyObject, D>: _RXDelegateProxy {
public typealias ParentObject = P
public typealias Delegate = D
private var _sentMessageForSelector = [Selector: MessageDispatcher]()
private var _methodInvokedForSelector = [Selector: MessageDispatcher]()
/// Parent object associated with delegate proxy.
private weak var _parentObject: ParentObject?
private let _currentDelegateFor: (ParentObject) -> AnyObject?
private let _setCurrentDelegateTo: (AnyObject?, ParentObject) -> Void
/// Initializes new instance.
///
/// - parameter parentObject: Optional parent object that owns `DelegateProxy` as associated object.
public init<Proxy: DelegateProxyType>(parentObject: ParentObject, delegateProxy: Proxy.Type)
where Proxy: DelegateProxy<ParentObject, Delegate>, Proxy.ParentObject == ParentObject, Proxy.Delegate == Delegate {
self._parentObject = parentObject
self._currentDelegateFor = delegateProxy._currentDelegate
self._setCurrentDelegateTo = delegateProxy._setCurrentDelegate
MainScheduler.ensureRunningOnMainThread()
#if TRACE_RESOURCES
_ = Resources.incrementTotal()
#endif
super.init()
}
/**
Returns observable sequence of invocations of delegate methods. Elements are sent *before method is invoked*.
Only methods that have `void` return value can be observed using this method because
those methods are used as a notification mechanism. It doesn't matter if they are optional
or not. Observing is performed by installing a hidden associated `PublishSubject` that is
used to dispatch messages to observers.
Delegate methods that have non `void` return value can't be observed directly using this method
because:
* those methods are not intended to be used as a notification mechanism, but as a behavior customization mechanism
* there is no sensible automatic way to determine a default return value
In case observing of delegate methods that have return type is required, it can be done by
manually installing a `PublishSubject` or `BehaviorSubject` and implementing delegate method.
e.g.
// delegate proxy part (RxScrollViewDelegateProxy)
let internalSubject = PublishSubject<CGPoint>
public func requiredDelegateMethod(scrollView: UIScrollView, arg1: CGPoint) -> Bool {
internalSubject.on(.next(arg1))
return self._forwardToDelegate?.requiredDelegateMethod?(scrollView, arg1: arg1) ?? defaultReturnValue
}
....
// reactive property implementation in a real class (`UIScrollView`)
public var property: Observable<CGPoint> {
let proxy = RxScrollViewDelegateProxy.proxy(for: base)
return proxy.internalSubject.asObservable()
}
**In case calling this method prints "Delegate proxy is already implementing `\(selector)`,
a more performant way of registering might exist.", that means that manual observing method
is required analog to the example above because delegate method has already been implemented.**
- parameter selector: Selector used to filter observed invocations of delegate methods.
- returns: Observable sequence of arguments passed to `selector` method.
*/
open func sentMessage(_ selector: Selector) -> Observable<[Any]> {
MainScheduler.ensureRunningOnMainThread()
let subject = self._sentMessageForSelector[selector]
if let subject = subject {
return subject.asObservable()
}
else {
let subject = MessageDispatcher(selector: selector, delegateProxy: self)
self._sentMessageForSelector[selector] = subject
return subject.asObservable()
}
}
/**
Returns observable sequence of invoked delegate methods. Elements are sent *after method is invoked*.
Only methods that have `void` return value can be observed using this method because
those methods are used as a notification mechanism. It doesn't matter if they are optional
or not. Observing is performed by installing a hidden associated `PublishSubject` that is
used to dispatch messages to observers.
Delegate methods that have non `void` return value can't be observed directly using this method
because:
* those methods are not intended to be used as a notification mechanism, but as a behavior customization mechanism
* there is no sensible automatic way to determine a default return value
In case observing of delegate methods that have return type is required, it can be done by
manually installing a `PublishSubject` or `BehaviorSubject` and implementing delegate method.
e.g.
// delegate proxy part (RxScrollViewDelegateProxy)
let internalSubject = PublishSubject<CGPoint>
public func requiredDelegateMethod(scrollView: UIScrollView, arg1: CGPoint) -> Bool {
internalSubject.on(.next(arg1))
return self._forwardToDelegate?.requiredDelegateMethod?(scrollView, arg1: arg1) ?? defaultReturnValue
}
....
// reactive property implementation in a real class (`UIScrollView`)
public var property: Observable<CGPoint> {
let proxy = RxScrollViewDelegateProxy.proxy(for: base)
return proxy.internalSubject.asObservable()
}
**In case calling this method prints "Delegate proxy is already implementing `\(selector)`,
a more performant way of registering might exist.", that means that manual observing method
is required analog to the example above because delegate method has already been implemented.**
- parameter selector: Selector used to filter observed invocations of delegate methods.
- returns: Observable sequence of arguments passed to `selector` method.
*/
open func methodInvoked(_ selector: Selector) -> Observable<[Any]> {
MainScheduler.ensureRunningOnMainThread()
let subject = self._methodInvokedForSelector[selector]
if let subject = subject {
return subject.asObservable()
}
else {
let subject = MessageDispatcher(selector: selector, delegateProxy: self)
self._methodInvokedForSelector[selector] = subject
return subject.asObservable()
}
}
fileprivate func checkSelectorIsObservable(_ selector: Selector) {
MainScheduler.ensureRunningOnMainThread()
if self.hasWiredImplementation(for: selector) {
print("⚠️ Delegate proxy is already implementing `\(selector)`, a more performant way of registering might exist.")
return
}
if self.voidDelegateMethodsContain(selector) {
return
}
// In case `_forwardToDelegate` is `nil`, it is assumed the check is being done prematurely.
if !(self._forwardToDelegate?.responds(to: selector) ?? true) {
print("⚠️ Using delegate proxy dynamic interception method but the target delegate object doesn't respond to the requested selector. " +
"In case pure Swift delegate proxy is being used please use manual observing method by using`PublishSubject`s. " +
" (selector: `\(selector)`, forwardToDelegate: `\(self._forwardToDelegate ?? self)`)")
}
}
// proxy
open override func _sentMessage(_ selector: Selector, withArguments arguments: [Any]) {
self._sentMessageForSelector[selector]?.on(.next(arguments))
}
open override func _methodInvoked(_ selector: Selector, withArguments arguments: [Any]) {
self._methodInvokedForSelector[selector]?.on(.next(arguments))
}
/// Returns reference of normal delegate that receives all forwarded messages
/// through `self`.
///
/// - returns: Value of reference if set or nil.
open func forwardToDelegate() -> Delegate? {
return castOptionalOrFatalError(self._forwardToDelegate)
}
/// Sets reference of normal delegate that receives all forwarded messages
/// through `self`.
///
/// - parameter delegate: Reference of delegate that receives all messages through `self`.
/// - parameter retainDelegate: Should `self` retain `forwardToDelegate`.
open func setForwardToDelegate(_ delegate: Delegate?, retainDelegate: Bool) {
#if DEBUG // 4.0 all configurations
MainScheduler.ensureRunningOnMainThread()
#endif
self._setForwardToDelegate(delegate, retainDelegate: retainDelegate)
let sentSelectors: [Selector] = self._sentMessageForSelector.values.filter { $0.hasObservers }.map { $0.selector }
let invokedSelectors: [Selector] = self._methodInvokedForSelector.values.filter { $0.hasObservers }.map { $0.selector }
let allUsedSelectors = sentSelectors + invokedSelectors
for selector in Set(allUsedSelectors) {
self.checkSelectorIsObservable(selector)
}
self.reset()
}
private func hasObservers(selector: Selector) -> Bool {
return (self._sentMessageForSelector[selector]?.hasObservers ?? false)
|| (self._methodInvokedForSelector[selector]?.hasObservers ?? false)
}
override open func responds(to aSelector: Selector!) -> Bool {
guard let aSelector = aSelector else { return false }
return super.responds(to: aSelector)
|| (self._forwardToDelegate?.responds(to: aSelector) ?? false)
|| (self.voidDelegateMethodsContain(aSelector) && self.hasObservers(selector: aSelector))
}
fileprivate func reset() {
guard let parentObject = self._parentObject else { return }
let maybeCurrentDelegate = self._currentDelegateFor(parentObject)
if maybeCurrentDelegate === self {
self._setCurrentDelegateTo(nil, parentObject)
self._setCurrentDelegateTo(castOrFatalError(self), parentObject)
}
}
deinit {
for v in self._sentMessageForSelector.values {
v.on(.completed)
}
for v in self._methodInvokedForSelector.values {
v.on(.completed)
}
#if TRACE_RESOURCES
_ = Resources.decrementTotal()
#endif
}
}
private let mainScheduler = MainScheduler()
private final class MessageDispatcher {
private let dispatcher: PublishSubject<[Any]>
private let result: Observable<[Any]>
fileprivate let selector: Selector
init<P, D>(selector: Selector, delegateProxy _delegateProxy: DelegateProxy<P, D>) {
weak var weakDelegateProxy = _delegateProxy
let dispatcher = PublishSubject<[Any]>()
self.dispatcher = dispatcher
self.selector = selector
self.result = dispatcher
.do(onSubscribed: { weakDelegateProxy?.checkSelectorIsObservable(selector); weakDelegateProxy?.reset() }, onDispose: { weakDelegateProxy?.reset() })
.share()
.subscribe(on: mainScheduler)
}
var on: (Event<[Any]>) -> Void {
return self.dispatcher.on
}
var hasObservers: Bool {
return self.dispatcher.hasObservers
}
func asObservable() -> Observable<[Any]> {
return self.result
}
}
#endif

View File

@@ -0,0 +1,435 @@
//
// DelegateProxyType.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 6/15/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if !os(Linux)
import Foundation
import RxSwift
/**
`DelegateProxyType` protocol enables using both normal delegates and Rx observable sequences with
views that can have only one delegate/datasource registered.
`Proxies` store information about observers, subscriptions and delegates
for specific views.
Type implementing `DelegateProxyType` should never be initialized directly.
To fetch initialized instance of type implementing `DelegateProxyType`, `proxy` method
should be used.
This is more or less how it works.
+-------------------------------------------+
| |
| UIView subclass (UIScrollView) |
| |
+-----------+-------------------------------+
|
| Delegate
|
|
+-----------v-------------------------------+
| |
| Delegate proxy : DelegateProxyType +-----+----> Observable<T1>
| , UIScrollViewDelegate | |
+-----------+-------------------------------+ +----> Observable<T2>
| |
| +----> Observable<T3>
| |
| forwards events |
| to custom delegate |
| v
+-----------v-------------------------------+
| |
| Custom delegate (UIScrollViewDelegate) |
| |
+-------------------------------------------+
Since RxCocoa needs to automagically create those Proxies and because views that have delegates can be hierarchical
UITableView : UIScrollView : UIView
.. and corresponding delegates are also hierarchical
UITableViewDelegate : UIScrollViewDelegate : NSObject
... this mechanism can be extended by using the following snippet in `registerKnownImplementations` or in some other
part of your app that executes before using `rx.*` (e.g. appDidFinishLaunching).
RxScrollViewDelegateProxy.register { RxTableViewDelegateProxy(parentObject: $0) }
*/
public protocol DelegateProxyType: AnyObject {
associatedtype ParentObject: AnyObject
associatedtype Delegate
/// It is require that enumerate call `register` of the extended DelegateProxy subclasses here.
static func registerKnownImplementations()
/// Unique identifier for delegate
static var identifier: UnsafeRawPointer { get }
/// Returns designated delegate property for object.
///
/// Objects can have multiple delegate properties.
///
/// Each delegate property needs to have it's own type implementing `DelegateProxyType`.
///
/// It's abstract method.
///
/// - parameter object: Object that has delegate property.
/// - returns: Value of delegate property.
static func currentDelegate(for object: ParentObject) -> Delegate?
/// Sets designated delegate property for object.
///
/// Objects can have multiple delegate properties.
///
/// Each delegate property needs to have it's own type implementing `DelegateProxyType`.
///
/// It's abstract method.
///
/// - parameter delegate: Delegate value.
/// - parameter object: Object that has delegate property.
static func setCurrentDelegate(_ delegate: Delegate?, to object: ParentObject)
/// Returns reference of normal delegate that receives all forwarded messages
/// through `self`.
///
/// - returns: Value of reference if set or nil.
func forwardToDelegate() -> Delegate?
/// Sets reference of normal delegate that receives all forwarded messages
/// through `self`.
///
/// - parameter forwardToDelegate: Reference of delegate that receives all messages through `self`.
/// - parameter retainDelegate: Should `self` retain `forwardToDelegate`.
func setForwardToDelegate(_ forwardToDelegate: Delegate?, retainDelegate: Bool)
}
// default implementations
extension DelegateProxyType {
/// Unique identifier for delegate
public static var identifier: UnsafeRawPointer {
let delegateIdentifier = ObjectIdentifier(Delegate.self)
let integerIdentifier = Int(bitPattern: delegateIdentifier)
return UnsafeRawPointer(bitPattern: integerIdentifier)!
}
}
// workaround of Delegate: class
extension DelegateProxyType {
static func _currentDelegate(for object: ParentObject) -> AnyObject? {
currentDelegate(for: object).map { $0 as AnyObject }
}
static func _setCurrentDelegate(_ delegate: AnyObject?, to object: ParentObject) {
setCurrentDelegate(castOptionalOrFatalError(delegate), to: object)
}
func _forwardToDelegate() -> AnyObject? {
self.forwardToDelegate().map { $0 as AnyObject }
}
func _setForwardToDelegate(_ forwardToDelegate: AnyObject?, retainDelegate: Bool) {
self.setForwardToDelegate(castOptionalOrFatalError(forwardToDelegate), retainDelegate: retainDelegate)
}
}
extension DelegateProxyType {
/// Store DelegateProxy subclass to factory.
/// When make 'Rx*DelegateProxy' subclass, call 'Rx*DelegateProxySubclass.register(for:_)' 1 time, or use it in DelegateProxyFactory
/// 'Rx*DelegateProxy' can have one subclass implementation per concrete ParentObject type.
/// Should call it from concrete DelegateProxy type, not generic.
public static func register<Parent>(make: @escaping (Parent) -> Self) {
self.factory.extend(make: make)
}
/// Creates new proxy for target object.
/// Should not call this function directory, use 'DelegateProxy.proxy(for:)'
public static func createProxy(for object: AnyObject) -> Self {
castOrFatalError(factory.createProxy(for: object))
}
/// Returns existing proxy for object or installs new instance of delegate proxy.
///
/// - parameter object: Target object on which to install delegate proxy.
/// - returns: Installed instance of delegate proxy.
///
///
/// extension Reactive where Base: UISearchBar {
///
/// public var delegate: DelegateProxy<UISearchBar, UISearchBarDelegate> {
/// return RxSearchBarDelegateProxy.proxy(for: base)
/// }
///
/// public var text: ControlProperty<String> {
/// let source: Observable<String> = self.delegate.observe(#selector(UISearchBarDelegate.searchBar(_:textDidChange:)))
/// ...
/// }
/// }
public static func proxy(for object: ParentObject) -> Self {
MainScheduler.ensureRunningOnMainThread()
let maybeProxy = self.assignedProxy(for: object)
let proxy: AnyObject
if let existingProxy = maybeProxy {
proxy = existingProxy
}
else {
proxy = castOrFatalError(self.createProxy(for: object))
self.assignProxy(proxy, toObject: object)
assert(self.assignedProxy(for: object) === proxy)
}
let currentDelegate = self._currentDelegate(for: object)
let delegateProxy: Self = castOrFatalError(proxy)
if currentDelegate !== delegateProxy {
delegateProxy._setForwardToDelegate(currentDelegate, retainDelegate: false)
assert(delegateProxy._forwardToDelegate() === currentDelegate)
self._setCurrentDelegate(proxy, to: object)
assert(self._currentDelegate(for: object) === proxy)
assert(delegateProxy._forwardToDelegate() === currentDelegate)
}
return delegateProxy
}
/// Sets forward delegate for `DelegateProxyType` associated with a specific object and return disposable that can be used to unset the forward to delegate.
/// Using this method will also make sure that potential original object cached selectors are cleared and will report any accidental forward delegate mutations.
///
/// - parameter forwardDelegate: Delegate object to set.
/// - parameter retainDelegate: Retain `forwardDelegate` while it's being set.
/// - parameter onProxyForObject: Object that has `delegate` property.
/// - returns: Disposable object that can be used to clear forward delegate.
public static func installForwardDelegate(_ forwardDelegate: Delegate, retainDelegate: Bool, onProxyForObject object: ParentObject) -> Disposable {
weak var weakForwardDelegate: AnyObject? = forwardDelegate as AnyObject
let proxy = self.proxy(for: object)
assert(proxy._forwardToDelegate() === nil, "This is a feature to warn you that there is already a delegate (or data source) set somewhere previously. The action you are trying to perform will clear that delegate (data source) and that means that some of your features that depend on that delegate (data source) being set will likely stop working.\n" +
"If you are ok with this, try to set delegate (data source) to `nil` in front of this operation.\n" +
" This is the source object value: \(object)\n" +
" This is the original delegate (data source) value: \(proxy.forwardToDelegate()!)\n" +
"Hint: Maybe delegate was already set in xib or storyboard and now it's being overwritten in code.\n")
proxy.setForwardToDelegate(forwardDelegate, retainDelegate: retainDelegate)
return Disposables.create {
MainScheduler.ensureRunningOnMainThread()
let delegate: AnyObject? = weakForwardDelegate
assert(delegate == nil || proxy._forwardToDelegate() === delegate, "Delegate was changed from time it was first set. Current \(String(describing: proxy.forwardToDelegate())), and it should have been \(proxy)")
proxy.setForwardToDelegate(nil, retainDelegate: retainDelegate)
}
}
}
// private extensions
extension DelegateProxyType {
private static var factory: DelegateProxyFactory {
DelegateProxyFactory.sharedFactory(for: self)
}
private static func assignedProxy(for object: ParentObject) -> AnyObject? {
let maybeDelegate = objc_getAssociatedObject(object, self.identifier)
return castOptionalOrFatalError(maybeDelegate)
}
private static func assignProxy(_ proxy: AnyObject, toObject object: ParentObject) {
objc_setAssociatedObject(object, self.identifier, proxy, .OBJC_ASSOCIATION_RETAIN)
}
}
/// Describes an object that has a delegate.
public protocol HasDelegate: AnyObject {
/// Delegate type
associatedtype Delegate
/// Delegate
var delegate: Delegate? { get set }
}
extension DelegateProxyType where ParentObject: HasDelegate, Self.Delegate == ParentObject.Delegate {
public static func currentDelegate(for object: ParentObject) -> Delegate? {
object.delegate
}
public static func setCurrentDelegate(_ delegate: Delegate?, to object: ParentObject) {
object.delegate = delegate
}
}
/// Describes an object that has a data source.
public protocol HasDataSource: AnyObject {
/// Data source type
associatedtype DataSource
/// Data source
var dataSource: DataSource? { get set }
}
extension DelegateProxyType where ParentObject: HasDataSource, Self.Delegate == ParentObject.DataSource {
public static func currentDelegate(for object: ParentObject) -> Delegate? {
object.dataSource
}
public static func setCurrentDelegate(_ delegate: Delegate?, to object: ParentObject) {
object.dataSource = delegate
}
}
/// Describes an object that has a prefetch data source.
@available(iOS 10.0, tvOS 10.0, *)
public protocol HasPrefetchDataSource: AnyObject {
/// Prefetch data source type
associatedtype PrefetchDataSource
/// Prefetch data source
var prefetchDataSource: PrefetchDataSource? { get set }
}
@available(iOS 10.0, tvOS 10.0, *)
extension DelegateProxyType where ParentObject: HasPrefetchDataSource, Self.Delegate == ParentObject.PrefetchDataSource {
public static func currentDelegate(for object: ParentObject) -> Delegate? {
object.prefetchDataSource
}
public static func setCurrentDelegate(_ delegate: Delegate?, to object: ParentObject) {
object.prefetchDataSource = delegate
}
}
#if os(iOS) || os(tvOS)
import UIKit
extension ObservableType {
func subscribeProxyDataSource<DelegateProxy: DelegateProxyType>(ofObject object: DelegateProxy.ParentObject, dataSource: DelegateProxy.Delegate, retainDataSource: Bool, binding: @escaping (DelegateProxy, Event<Element>) -> Void)
-> Disposable
where DelegateProxy.ParentObject: UIView
, DelegateProxy.Delegate: AnyObject {
let proxy = DelegateProxy.proxy(for: object)
let unregisterDelegate = DelegateProxy.installForwardDelegate(dataSource, retainDelegate: retainDataSource, onProxyForObject: object)
// Do not perform layoutIfNeeded if the object is still not in the view hierarchy
if object.window != nil {
// this is needed to flush any delayed old state (https://github.com/RxSwiftCommunity/RxDataSources/pull/75)
object.layoutIfNeeded()
}
let subscription = self.asObservable()
.observe(on:MainScheduler())
.catch { error in
bindingError(error)
return Observable.empty()
}
// source can never end, otherwise it would release the subscriber, and deallocate the data source
.concat(Observable.never())
.take(until: object.rx.deallocated)
.subscribe { [weak object] (event: Event<Element>) in
if let object = object {
assert(proxy === DelegateProxy.currentDelegate(for: object), "Proxy changed from the time it was first set.\nOriginal: \(proxy)\nExisting: \(String(describing: DelegateProxy.currentDelegate(for: object)))")
}
binding(proxy, event)
switch event {
case .error(let error):
bindingError(error)
unregisterDelegate.dispose()
case .completed:
unregisterDelegate.dispose()
default:
break
}
}
return Disposables.create { [weak object] in
subscription.dispose()
if object?.window != nil {
object?.layoutIfNeeded()
}
unregisterDelegate.dispose()
}
}
}
#endif
/**
To add delegate proxy subclasses call `DelegateProxySubclass.register()` in `registerKnownImplementations` or in some other
part of your app that executes before using `rx.*` (e.g. appDidFinishLaunching).
class RxScrollViewDelegateProxy: DelegateProxy {
public static func registerKnownImplementations() {
self.register { RxTableViewDelegateProxy(parentObject: $0) }
}
...
*/
private class DelegateProxyFactory {
private static var _sharedFactories: [UnsafeRawPointer: DelegateProxyFactory] = [:]
fileprivate static func sharedFactory<DelegateProxy: DelegateProxyType>(for proxyType: DelegateProxy.Type) -> DelegateProxyFactory {
MainScheduler.ensureRunningOnMainThread()
let identifier = DelegateProxy.identifier
if let factory = _sharedFactories[identifier] {
return factory
}
let factory = DelegateProxyFactory(for: proxyType)
_sharedFactories[identifier] = factory
DelegateProxy.registerKnownImplementations()
return factory
}
private var _factories: [ObjectIdentifier: ((AnyObject) -> AnyObject)]
private var _delegateProxyType: Any.Type
private var _identifier: UnsafeRawPointer
private init<DelegateProxy: DelegateProxyType>(for proxyType: DelegateProxy.Type) {
self._factories = [:]
self._delegateProxyType = proxyType
self._identifier = proxyType.identifier
}
fileprivate func extend<DelegateProxy: DelegateProxyType, ParentObject>(make: @escaping (ParentObject) -> DelegateProxy) {
MainScheduler.ensureRunningOnMainThread()
precondition(self._identifier == DelegateProxy.identifier, "Delegate proxy has inconsistent identifier")
guard self._factories[ObjectIdentifier(ParentObject.self)] == nil else {
rxFatalError("The factory of \(ParentObject.self) is duplicated. DelegateProxy is not allowed of duplicated base object type.")
}
self._factories[ObjectIdentifier(ParentObject.self)] = { make(castOrFatalError($0)) }
}
fileprivate func createProxy(for object: AnyObject) -> AnyObject {
MainScheduler.ensureRunningOnMainThread()
var maybeMirror: Mirror? = Mirror(reflecting: object)
while let mirror = maybeMirror {
if let factory = self._factories[ObjectIdentifier(mirror.subjectType)] {
return factory(object)
}
maybeMirror = mirror.superclassMirror
}
rxFatalError("DelegateProxy has no factory of \(object). Implement DelegateProxy subclass for \(object) first.")
}
}
#endif

View File

@@ -0,0 +1,148 @@
//
// Infallible+Bind.swift
// RxCocoa
//
// Created by Shai Mishali on 27/08/2020.
// Copyright © 2020 Krunoslav Zaher. All rights reserved.
//
import RxSwift
extension InfallibleType {
/**
Creates new subscription and sends elements to observer(s).
In this form, it's equivalent to the `subscribe` method, but it better conveys intent, and enables
writing more consistent binding code.
- parameter observers: Observers to receives events.
- returns: Disposable object that can be used to unsubscribe the observers.
*/
public func bind<Observer: ObserverType>(to observers: Observer...) -> Disposable where Observer.Element == Element {
self.subscribe { event in
observers.forEach { $0.on(event) }
}
}
/**
Creates new subscription and sends elements to observer(s).
In this form, it's equivalent to the `subscribe` method, but it better conveys intent, and enables
writing more consistent binding code.
- parameter observers: Observers to receives events.
- returns: Disposable object that can be used to unsubscribe the observers.
*/
public func bind<Observer: ObserverType>(to observers: Observer...) -> Disposable where Observer.Element == Element? {
self.map { $0 as Element? }
.subscribe { event in
observers.forEach { $0.on(event) }
}
}
/**
Subscribes to observable sequence using custom binder function.
- parameter binder: Function used to bind elements from `self`.
- returns: Object representing subscription.
*/
public func bind<Result>(to binder: (Self) -> Result) -> Result {
binder(self)
}
/**
Subscribes to observable sequence using custom binder function and final parameter passed to binder function
after `self` is passed.
public func bind<R1, R2>(to binder: Self -> R1 -> R2, curriedArgument: R1) -> R2 {
return binder(self)(curriedArgument)
}
- parameter binder: Function used to bind elements from `self`.
- parameter curriedArgument: Final argument passed to `binder` to finish binding process.
- returns: Object representing subscription.
*/
public func bind<R1, R2>(to binder: (Self) -> (R1) -> R2, curriedArgument: R1) -> R2 {
binder(self)(curriedArgument)
}
/**
Subscribes an element handler to an observable sequence.
In case error occurs in debug mode, `fatalError` will be raised.
In case error occurs in release mode, `error` will be logged.
- parameter onNext: Action to invoke for each element in the observable sequence.
- returns: Subscription object used to unsubscribe from the observable sequence.
*/
public func bind(onNext: @escaping (Element) -> Void) -> Disposable {
self.subscribe(onNext: onNext)
}
/**
Creates new subscription and sends elements to `BehaviorRelay`.
- parameter relays: Target relay for sequence elements.
- returns: Disposable object that can be used to unsubscribe the observer from the relay.
*/
public func bind(to relays: BehaviorRelay<Element>...) -> Disposable {
return self.subscribe(onNext: { e in
relays.forEach { $0.accept(e) }
})
}
/**
Creates new subscription and sends elements to `BehaviorRelay`.
- parameter relays: Target relay for sequence elements.
- returns: Disposable object that can be used to unsubscribe the observer from the relay.
*/
public func bind(to relays: BehaviorRelay<Element?>...) -> Disposable {
return self.subscribe(onNext: { e in
relays.forEach { $0.accept(e) }
})
}
/**
Creates new subscription and sends elements to `PublishRelay`.
- parameter relays: Target relay for sequence elements.
- returns: Disposable object that can be used to unsubscribe the observer from the relay.
*/
public func bind(to relays: PublishRelay<Element>...) -> Disposable {
return self.subscribe(onNext: { e in
relays.forEach { $0.accept(e) }
})
}
/**
Creates new subscription and sends elements to `PublishRelay`.
- parameter relays: Target relay for sequence elements.
- returns: Disposable object that can be used to unsubscribe the observer from the relay.
*/
public func bind(to relays: PublishRelay<Element?>...) -> Disposable {
return self.subscribe(onNext: { e in
relays.forEach { $0.accept(e) }
})
}
/**
Creates new subscription and sends elements to `ReplayRelay`.
- parameter relays: Target relay for sequence elements.
- returns: Disposable object that can be used to unsubscribe the observer from the relay.
*/
public func bind(to relays: ReplayRelay<Element>...) -> Disposable {
return self.subscribe(onNext: { e in
relays.forEach { $0.accept(e) }
})
}
/**
Creates new subscription and sends elements to `ReplayRelay`.
- parameter relays: Target relay for sequence elements.
- returns: Disposable object that can be used to unsubscribe the observer from the relay.
*/
public func bind(to relays: ReplayRelay<Element?>...) -> Disposable {
return self.subscribe(onNext: { e in
relays.forEach { $0.accept(e) }
})
}
}

View File

@@ -0,0 +1,103 @@
//
// Observable+Bind.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 8/29/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import RxSwift
extension ObservableType {
/**
Creates new subscription and sends elements to observer(s).
In this form, it's equivalent to the `subscribe` method, but it better conveys intent, and enables
writing more consistent binding code.
- parameter observers: Observers to receives events.
- returns: Disposable object that can be used to unsubscribe the observers.
*/
public func bind<Observer: ObserverType>(to observers: Observer...) -> Disposable where Observer.Element == Element {
self.subscribe { event in
observers.forEach { $0.on(event) }
}
}
/**
Creates new subscription and sends elements to observer(s).
In this form, it's equivalent to the `subscribe` method, but it better conveys intent, and enables
writing more consistent binding code.
- parameter observers: Observers to receives events.
- returns: Disposable object that can be used to unsubscribe the observers.
*/
public func bind<Observer: ObserverType>(to observers: Observer...) -> Disposable where Observer.Element == Element? {
self.map { $0 as Element? }
.subscribe { event in
observers.forEach { $0.on(event) }
}
}
/**
Subscribes to observable sequence using custom binder function.
- parameter binder: Function used to bind elements from `self`.
- returns: Object representing subscription.
*/
public func bind<Result>(to binder: (Self) -> Result) -> Result {
binder(self)
}
/**
Subscribes to observable sequence using custom binder function and final parameter passed to binder function
after `self` is passed.
public func bind<R1, R2>(to binder: Self -> R1 -> R2, curriedArgument: R1) -> R2 {
return binder(self)(curriedArgument)
}
- parameter binder: Function used to bind elements from `self`.
- parameter curriedArgument: Final argument passed to `binder` to finish binding process.
- returns: Object representing subscription.
*/
public func bind<R1, R2>(to binder: (Self) -> (R1) -> R2, curriedArgument: R1) -> R2 {
binder(self)(curriedArgument)
}
/**
Subscribes an element handler to an observable sequence.
In case error occurs in debug mode, `fatalError` will be raised.
In case error occurs in release mode, `error` will be logged.
- Note: If `object` can't be retained, none of the other closures will be invoked.
- parameter object: The object to provide an unretained reference on.
- parameter onNext: Action to invoke for each element in the observable sequence.
- returns: Subscription object used to unsubscribe from the observable sequence.
*/
public func bind<Object: AnyObject>(
with object: Object,
onNext: @escaping (Object, Element) -> Void
) -> Disposable {
self.subscribe(onNext: { [weak object] in
guard let object = object else { return }
onNext(object, $0)
},
onError: { error in
rxFatalErrorInDebug("Binding error: \(error)")
})
}
/**
Subscribes an element handler to an observable sequence.
In case error occurs in debug mode, `fatalError` will be raised.
In case error occurs in release mode, `error` will be logged.
- parameter onNext: Action to invoke for each element in the observable sequence.
- returns: Subscription object used to unsubscribe from the observable sequence.
*/
public func bind(onNext: @escaping (Element) -> Void) -> Disposable {
self.subscribe(onNext: onNext,
onError: { error in
rxFatalErrorInDebug("Binding error: \(error)")
})
}
}

View File

@@ -0,0 +1,161 @@
//
// RxCocoaObjCRuntimeError+Extensions.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 10/9/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if SWIFT_PACKAGE && !DISABLE_SWIZZLING && !os(Linux)
import RxCocoaRuntime
#endif
#if !DISABLE_SWIZZLING && !os(Linux)
/// RxCocoa ObjC runtime interception mechanism.
public enum RxCocoaInterceptionMechanism {
/// Unknown message interception mechanism.
case unknown
/// Key value observing interception mechanism.
case kvo
}
/// RxCocoa ObjC runtime modification errors.
public enum RxCocoaObjCRuntimeError
: Swift.Error
, CustomDebugStringConvertible {
/// Unknown error has occurred.
case unknown(target: AnyObject)
/**
If the object is reporting a different class then it's real class, that means that there is probably
already some interception mechanism in place or something weird is happening.
The most common case when this would happen is when using a combination of KVO (`observe`) and `sentMessage`.
This error is easily resolved by just using `sentMessage` observing before `observe`.
The reason why the other way around could create issues is because KVO will unregister it's interceptor
class and restore original class. Unfortunately that will happen no matter was there another interceptor
subclass registered in hierarchy or not.
Failure scenario:
* KVO sets class to be `__KVO__OriginalClass` (subclass of `OriginalClass`)
* `sentMessage` sets object class to be `_RX_namespace___KVO__OriginalClass` (subclass of `__KVO__OriginalClass`)
* then unobserving with KVO will restore class to be `OriginalClass` -> failure point (possibly a bug in KVO)
The reason why changing order of observing works is because any interception method on unregistration
should return object's original real class (if that doesn't happen then it's really easy to argue that's a bug
in that interception mechanism).
This library won't remove registered interceptor even if there aren't any observers left because
it's highly unlikely it would have any benefit in real world use cases, and it's even more
dangerous.
*/
case objectMessagesAlreadyBeingIntercepted(target: AnyObject, interceptionMechanism: RxCocoaInterceptionMechanism)
/// Trying to observe messages for selector that isn't implemented.
case selectorNotImplemented(target: AnyObject)
/// Core Foundation classes are usually toll free bridged. Those classes crash the program in case
/// `object_setClass` is performed on them.
///
/// There is a possibility to just swizzle methods on original object, but since those won't be usual use
/// cases for this library, then an error will just be reported for now.
case cantInterceptCoreFoundationTollFreeBridgedObjects(target: AnyObject)
/// Two libraries have simultaneously tried to modify ObjC runtime and that was detected. This can only
/// happen in scenarios where multiple interception libraries are used.
///
/// To synchronize other libraries intercepting messages for an object, use `synchronized` on target object and
/// it's meta-class.
case threadingCollisionWithOtherInterceptionMechanism(target: AnyObject)
/// For some reason saving original method implementation under RX namespace failed.
case savingOriginalForwardingMethodFailed(target: AnyObject)
/// Intercepting a sent message by replacing a method implementation with `_objc_msgForward` failed for some reason.
case replacingMethodWithForwardingImplementation(target: AnyObject)
/// Attempt to intercept one of the performance sensitive methods:
/// * class
/// * respondsToSelector:
/// * methodSignatureForSelector:
/// * forwardingTargetForSelector:
case observingPerformanceSensitiveMessages(target: AnyObject)
/// Message implementation has unsupported return type (for example large struct). The reason why this is a error
/// is because in some cases intercepting sent messages requires replacing implementation with `_objc_msgForward_stret`
/// instead of `_objc_msgForward`.
///
/// The unsupported cases should be fairly uncommon.
case observingMessagesWithUnsupportedReturnType(target: AnyObject)
}
extension RxCocoaObjCRuntimeError {
/// A textual representation of `self`, suitable for debugging.
public var debugDescription: String {
switch self {
case let .unknown(target):
return "Unknown error occurred.\nTarget: `\(target)`"
case let .objectMessagesAlreadyBeingIntercepted(target, interceptionMechanism):
let interceptionMechanismDescription = interceptionMechanism == .kvo ? "KVO" : "other interception mechanism"
return "Collision between RxCocoa interception mechanism and \(interceptionMechanismDescription)."
+ " To resolve this conflict please use this interception mechanism first.\nTarget: \(target)"
case let .selectorNotImplemented(target):
return "Trying to observe messages for selector that isn't implemented.\nTarget: \(target)"
case let .cantInterceptCoreFoundationTollFreeBridgedObjects(target):
return "Interception of messages sent to Core Foundation isn't supported.\nTarget: \(target)"
case let .threadingCollisionWithOtherInterceptionMechanism(target):
return "Detected a conflict while modifying ObjC runtime.\nTarget: \(target)"
case let .savingOriginalForwardingMethodFailed(target):
return "Saving original method implementation failed.\nTarget: \(target)"
case let .replacingMethodWithForwardingImplementation(target):
return "Intercepting a sent message by replacing a method implementation with `_objc_msgForward` failed for some reason.\nTarget: \(target)"
case let .observingPerformanceSensitiveMessages(target):
return "Attempt to intercept one of the performance sensitive methods. \nTarget: \(target)"
case let .observingMessagesWithUnsupportedReturnType(target):
return "Attempt to intercept a method with unsupported return type. \nTarget: \(target)"
}
}
}
// MARK: Conversions `NSError` > `RxCocoaObjCRuntimeError`
extension Error {
func rxCocoaErrorForTarget(_ target: AnyObject) -> RxCocoaObjCRuntimeError {
let error = self as NSError
if error.domain == RXObjCRuntimeErrorDomain {
let errorCode = RXObjCRuntimeError(rawValue: error.code) ?? .unknown
switch errorCode {
case .unknown:
return .unknown(target: target)
case .objectMessagesAlreadyBeingIntercepted:
let isKVO = (error.userInfo[RXObjCRuntimeErrorIsKVOKey] as? NSNumber)?.boolValue ?? false
return .objectMessagesAlreadyBeingIntercepted(target: target, interceptionMechanism: isKVO ? .kvo : .unknown)
case .selectorNotImplemented:
return .selectorNotImplemented(target: target)
case .cantInterceptCoreFoundationTollFreeBridgedObjects:
return .cantInterceptCoreFoundationTollFreeBridgedObjects(target: target)
case .threadingCollisionWithOtherInterceptionMechanism:
return .threadingCollisionWithOtherInterceptionMechanism(target: target)
case .savingOriginalForwardingMethodFailed:
return .savingOriginalForwardingMethodFailed(target: target)
case .replacingMethodWithForwardingImplementation:
return .replacingMethodWithForwardingImplementation(target: target)
case .observingPerformanceSensitiveMessages:
return .observingPerformanceSensitiveMessages(target: target)
case .observingMessagesWithUnsupportedReturnType:
return .observingMessagesWithUnsupportedReturnType(target: target)
@unknown default:
fatalError("Unhandled Objective C Runtime Error")
}
}
return RxCocoaObjCRuntimeError.unknown(target: target)
}
}
#endif

View File

@@ -0,0 +1,43 @@
//
// RxTarget.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 7/12/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation
import RxSwift
class RxTarget : NSObject
, Disposable {
private var retainSelf: RxTarget?
override init() {
super.init()
self.retainSelf = self
#if TRACE_RESOURCES
_ = Resources.incrementTotal()
#endif
#if DEBUG
MainScheduler.ensureRunningOnMainThread()
#endif
}
func dispose() {
#if DEBUG
MainScheduler.ensureRunningOnMainThread()
#endif
self.retainSelf = nil
}
#if TRACE_RESOURCES
deinit {
_ = Resources.decrementTotal()
}
#endif
}

View File

@@ -0,0 +1,20 @@
//
// SectionedViewDataSourceType.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 1/10/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
/// Data source with access to underlying sectioned model.
public protocol SectionedViewDataSourceType {
/// Returns model at index path.
///
/// In case data source doesn't contain any sections when this method is being called, `RxCocoaError.ItemsNotYetBound(object: self)` is thrown.
/// - parameter indexPath: Model index path
/// - returns: Model at index path.
func model(at indexPath: IndexPath) throws -> Any
}

View File

@@ -0,0 +1,78 @@
//
// TextInput.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 5/12/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import RxSwift
#if os(iOS) || os(tvOS)
import UIKit
/// Represents text input with reactive extensions.
public struct TextInput<Base: UITextInput> {
/// Base text input to extend.
public let base: Base
/// Reactive wrapper for `text` property.
public let text: ControlProperty<String?>
/// Initializes new text input.
///
/// - parameter base: Base object.
/// - parameter text: Textual control property.
public init(base: Base, text: ControlProperty<String?>) {
self.base = base
self.text = text
}
}
extension Reactive where Base: UITextField {
/// Reactive text input.
public var textInput: TextInput<Base> {
return TextInput(base: base, text: self.text)
}
}
extension Reactive where Base: UITextView {
/// Reactive text input.
public var textInput: TextInput<Base> {
return TextInput(base: base, text: self.text)
}
}
#endif
#if os(macOS)
import Cocoa
/// Represents text input with reactive extensions.
public struct TextInput<Base: NSTextInputClient> {
/// Base text input to extend.
public let base: Base
/// Reactive wrapper for `text` property.
public let text: ControlProperty<String?>
/// Initializes new text input.
///
/// - parameter base: Base object.
/// - parameter text: Textual control property.
public init(base: Base, text: ControlProperty<String?>) {
self.base = base
self.text = text
}
}
extension Reactive where Base: NSTextField, Base: NSTextInputClient {
/// Reactive text input.
public var textInput: TextInput<Base> {
return TextInput(base: self.base, text: self.text)
}
}
#endif

View File

@@ -0,0 +1,68 @@
//
// KVORepresentable+CoreGraphics.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 11/14/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if !os(Linux)
import RxSwift
import CoreGraphics
import Foundation
#if arch(x86_64) || arch(arm64)
let CGRectType = "{CGRect={CGPoint=dd}{CGSize=dd}}"
let CGSizeType = "{CGSize=dd}"
let CGPointType = "{CGPoint=dd}"
#elseif arch(i386) || arch(arm) || os(watchOS)
let CGRectType = "{CGRect={CGPoint=ff}{CGSize=ff}}"
let CGSizeType = "{CGSize=ff}"
let CGPointType = "{CGPoint=ff}"
#endif
extension CGRect : KVORepresentable {
public typealias KVOType = NSValue
/// Constructs self from `NSValue`.
public init?(KVOValue: KVOType) {
if strcmp(KVOValue.objCType, CGRectType) != 0 {
return nil
}
var typedValue = CGRect(x: 0, y: 0, width: 0, height: 0)
KVOValue.getValue(&typedValue)
self = typedValue
}
}
extension CGPoint : KVORepresentable {
public typealias KVOType = NSValue
/// Constructs self from `NSValue`.
public init?(KVOValue: KVOType) {
if strcmp(KVOValue.objCType, CGPointType) != 0 {
return nil
}
var typedValue = CGPoint(x: 0, y: 0)
KVOValue.getValue(&typedValue)
self = typedValue
}
}
extension CGSize : KVORepresentable {
public typealias KVOType = NSValue
/// Constructs self from `NSValue`.
public init?(KVOValue: KVOType) {
if strcmp(KVOValue.objCType, CGSizeType) != 0 {
return nil
}
var typedValue = CGSize(width: 0, height: 0)
KVOValue.getValue(&typedValue)
self = typedValue
}
}
#endif

View File

@@ -0,0 +1,88 @@
//
// KVORepresentable+Swift.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 11/14/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation
extension Int : KVORepresentable {
public typealias KVOType = NSNumber
/// Constructs `Self` using KVO value.
public init?(KVOValue: KVOType) {
self.init(KVOValue.int32Value)
}
}
extension Int32 : KVORepresentable {
public typealias KVOType = NSNumber
/// Constructs `Self` using KVO value.
public init?(KVOValue: KVOType) {
self.init(KVOValue.int32Value)
}
}
extension Int64 : KVORepresentable {
public typealias KVOType = NSNumber
/// Constructs `Self` using KVO value.
public init?(KVOValue: KVOType) {
self.init(KVOValue.int64Value)
}
}
extension UInt : KVORepresentable {
public typealias KVOType = NSNumber
/// Constructs `Self` using KVO value.
public init?(KVOValue: KVOType) {
self.init(KVOValue.uintValue)
}
}
extension UInt32 : KVORepresentable {
public typealias KVOType = NSNumber
/// Constructs `Self` using KVO value.
public init?(KVOValue: KVOType) {
self.init(KVOValue.uint32Value)
}
}
extension UInt64 : KVORepresentable {
public typealias KVOType = NSNumber
/// Constructs `Self` using KVO value.
public init?(KVOValue: KVOType) {
self.init(KVOValue.uint64Value)
}
}
extension Bool : KVORepresentable {
public typealias KVOType = NSNumber
/// Constructs `Self` using KVO value.
public init?(KVOValue: KVOType) {
self.init(KVOValue.boolValue)
}
}
extension RawRepresentable where RawValue: KVORepresentable {
/// Constructs `Self` using optional KVO value.
init?(KVOValue: RawValue.KVOType?) {
guard let KVOValue = KVOValue else {
return nil
}
guard let rawValue = RawValue(KVOValue: KVOValue) else {
return nil
}
self.init(rawValue: rawValue)
}
}

View File

@@ -0,0 +1,28 @@
//
// KVORepresentable.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 11/14/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
/// Type that is KVO representable (KVO mechanism can be used to observe it).
public protocol KVORepresentable {
/// Associated KVO type.
associatedtype KVOType
/// Constructs `Self` using KVO value.
init?(KVOValue: KVOType)
}
extension KVORepresentable {
/// Initializes `KVORepresentable` with optional value.
init?(KVOValue: KVOType?) {
guard let KVOValue = KVOValue else {
return nil
}
self.init(KVOValue: KVOValue)
}
}

View File

@@ -0,0 +1,60 @@
//
// NSObject+Rx+KVORepresentable.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 11/14/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if !os(Linux)
import Foundation
import RxSwift
/// Key value observing options
public struct KeyValueObservingOptions: OptionSet {
/// Raw value
public let rawValue: UInt
public init(rawValue: UInt) {
self.rawValue = rawValue
}
/// Whether a sequence element should be sent to the observer immediately, before the subscribe method even returns.
public static let initial = KeyValueObservingOptions(rawValue: 1 << 0)
/// Whether to send updated values.
public static let new = KeyValueObservingOptions(rawValue: 1 << 1)
}
extension Reactive where Base: NSObject {
/**
Specialization of generic `observe` method.
This is a special overload because to observe values of some type (for example `Int`), first values of KVO type
need to be observed (`NSNumber`), and then converted to result type.
For more information take a look at `observe` method.
*/
public func observe<Element: KVORepresentable>(_ type: Element.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial], retainSelf: Bool = true) -> Observable<Element?> {
return self.observe(Element.KVOType.self, keyPath, options: options, retainSelf: retainSelf)
.map(Element.init)
}
}
#if !DISABLE_SWIZZLING && !os(Linux)
// KVO
extension Reactive where Base: NSObject {
/**
Specialization of generic `observeWeakly` method.
For more information take a look at `observeWeakly` method.
*/
public func observeWeakly<Element: KVORepresentable>(_ type: Element.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial]) -> Observable<Element?> {
return self.observeWeakly(Element.KVOType.self, keyPath, options: options)
.map(Element.init)
}
}
#endif
#endif

View File

@@ -0,0 +1,52 @@
//
// NSObject+Rx+RawRepresentable.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 11/9/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if !os(Linux)
import RxSwift
import Foundation
extension Reactive where Base: NSObject {
/**
Specialization of generic `observe` method.
This specialization first observes `KVORepresentable` value and then converts it to `RawRepresentable` value.
It is useful for observing bridged ObjC enum values.
For more information take a look at `observe` method.
*/
public func observe<Element: RawRepresentable>(_ type: Element.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial], retainSelf: Bool = true) -> Observable<Element?> where Element.RawValue: KVORepresentable {
return self.observe(Element.RawValue.KVOType.self, keyPath, options: options, retainSelf: retainSelf)
.map(Element.init)
}
}
#if !DISABLE_SWIZZLING
// observeWeakly + RawRepresentable
extension Reactive where Base: NSObject {
/**
Specialization of generic `observeWeakly` method.
This specialization first observes `KVORepresentable` value and then converts it to `RawRepresentable` value.
It is useful for observing bridged ObjC enum values.
For more information take a look at `observeWeakly` method.
*/
public func observeWeakly<Element: RawRepresentable>(_ type: Element.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial]) -> Observable<Element?> where Element.RawValue: KVORepresentable {
return self.observeWeakly(Element.RawValue.KVOType.self, keyPath, options: options)
.map(Element.init)
}
}
#endif
#endif

View File

@@ -0,0 +1,569 @@
//
// NSObject+Rx.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 2/21/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if !os(Linux)
import Foundation
import RxSwift
#if SWIFT_PACKAGE && !DISABLE_SWIZZLING && !os(Linux)
import RxCocoaRuntime
#endif
#if !DISABLE_SWIZZLING && !os(Linux)
private var deallocatingSubjectTriggerContext: UInt8 = 0
private var deallocatingSubjectContext: UInt8 = 0
#endif
private var deallocatedSubjectTriggerContext: UInt8 = 0
private var deallocatedSubjectContext: UInt8 = 0
#if !os(Linux)
/**
KVO is a tricky mechanism.
When observing child in a ownership hierarchy, usually retaining observing target is wanted behavior.
When observing parent in a ownership hierarchy, usually retaining target isn't wanter behavior.
KVO with weak references is especially tricky. For it to work, some kind of swizzling is required.
That can be done by
* replacing object class dynamically (like KVO does)
* by swizzling `dealloc` method on all instances for a class.
* some third method ...
Both approaches can fail in certain scenarios:
* problems arise when swizzlers return original object class (like KVO does when nobody is observing)
* Problems can arise because replacing dealloc method isn't atomic operation (get implementation,
set implementation).
Second approach is chosen. It can fail in case there are multiple libraries dynamically trying
to replace dealloc method. In case that isn't the case, it should be ok.
*/
extension Reactive where Base: NSObject {
/**
Observes values on `keyPath` starting from `self` with `options` and retains `self` if `retainSelf` is set.
`observe` is just a simple and performant wrapper around KVO mechanism.
* it can be used to observe paths starting from `self` or from ancestors in ownership graph (`retainSelf = false`)
* it can be used to observe paths starting from descendants in ownership graph (`retainSelf = true`)
* the paths have to consist only of `strong` properties, otherwise you are risking crashing the system by not unregistering KVO observer before dealloc.
If support for weak properties is needed or observing arbitrary or unknown relationships in the
ownership tree, `observeWeakly` is the preferred option.
- parameter type: Optional type hint of the observed sequence elements.
- parameter keyPath: Key path of property names to observe.
- parameter options: KVO mechanism notification options.
- parameter retainSelf: Retains self during observation if set `true`.
- returns: Observable sequence of objects on `keyPath`.
*/
public func observe<Element>(_ type: Element.Type,
_ keyPath: String,
options: KeyValueObservingOptions = [.new, .initial],
retainSelf: Bool = true) -> Observable<Element?> {
KVOObservable(object: self.base, keyPath: keyPath, options: options, retainTarget: retainSelf).asObservable()
}
/**
Observes values at the provided key path using the provided options.
- parameter keyPath: A key path between the object and one of its properties.
- parameter options: Key-value observation options, defaults to `.new` and `.initial`.
- note: When the object is deallocated, a completion event is emitted.
- returns: An observable emitting value changes at the provided key path.
*/
public func observe<Element>(_ keyPath: KeyPath<Base, Element>,
options: NSKeyValueObservingOptions = [.new, .initial]) -> Observable<Element> {
Observable<Element>.create { [weak base] observer in
let observation = base?.observe(keyPath, options: options) { obj, _ in
observer.on(.next(obj[keyPath: keyPath]))
}
return Disposables.create { observation?.invalidate() }
}
.take(until: base.rx.deallocated)
}
}
#endif
#if !DISABLE_SWIZZLING && !os(Linux)
// KVO
extension Reactive where Base: NSObject {
/**
Observes values on `keyPath` starting from `self` with `options` and doesn't retain `self`.
It can be used in all cases where `observe` can be used and additionally
* because it won't retain observed target, it can be used to observe arbitrary object graph whose ownership relation is unknown
* it can be used to observe `weak` properties
**Since it needs to intercept object deallocation process it needs to perform swizzling of `dealloc` method on observed object.**
- parameter type: Optional type hint of the observed sequence elements.
- parameter keyPath: Key path of property names to observe.
- parameter options: KVO mechanism notification options.
- returns: Observable sequence of objects on `keyPath`.
*/
public func observeWeakly<Element>(_ type: Element.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial]) -> Observable<Element?> {
return observeWeaklyKeyPathFor(self.base, keyPath: keyPath, options: options)
.map { n in
return n as? Element
}
}
}
#endif
// Dealloc
extension Reactive where Base: AnyObject {
/**
Observable sequence of object deallocated events.
After object is deallocated one `()` element will be produced and sequence will immediately complete.
- returns: Observable sequence of object deallocated events.
*/
public var deallocated: Observable<Void> {
return self.synchronized {
if let deallocObservable = objc_getAssociatedObject(self.base, &deallocatedSubjectContext) as? DeallocObservable {
return deallocObservable.subject
}
let deallocObservable = DeallocObservable()
objc_setAssociatedObject(self.base, &deallocatedSubjectContext, deallocObservable, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return deallocObservable.subject
}
}
#if !DISABLE_SWIZZLING && !os(Linux)
/**
Observable sequence of message arguments that completes when object is deallocated.
Each element is produced before message is invoked on target object. `methodInvoked`
exists in case observing of invoked messages is needed.
In case an error occurs sequence will fail with `RxCocoaObjCRuntimeError`.
In case some argument is `nil`, instance of `NSNull()` will be sent.
- returns: Observable sequence of arguments passed to `selector` method.
*/
public func sentMessage(_ selector: Selector) -> Observable<[Any]> {
return self.synchronized {
// in case of dealloc selector replay subject behavior needs to be used
if selector == deallocSelector {
return self.deallocating.map { _ in [] }
}
do {
let proxy: MessageSentProxy = try self.registerMessageInterceptor(selector)
return proxy.messageSent.asObservable()
}
catch let e {
return Observable.error(e)
}
}
}
/**
Observable sequence of message arguments that completes when object is deallocated.
Each element is produced after message is invoked on target object. `sentMessage`
exists in case interception of sent messages before they were invoked is needed.
In case an error occurs sequence will fail with `RxCocoaObjCRuntimeError`.
In case some argument is `nil`, instance of `NSNull()` will be sent.
- returns: Observable sequence of arguments passed to `selector` method.
*/
public func methodInvoked(_ selector: Selector) -> Observable<[Any]> {
return self.synchronized {
// in case of dealloc selector replay subject behavior needs to be used
if selector == deallocSelector {
return self.deallocated.map { _ in [] }
}
do {
let proxy: MessageSentProxy = try self.registerMessageInterceptor(selector)
return proxy.methodInvoked.asObservable()
}
catch let e {
return Observable.error(e)
}
}
}
/**
Observable sequence of object deallocating events.
When `dealloc` message is sent to `self` one `()` element will be produced and after object is deallocated sequence
will immediately complete.
In case an error occurs sequence will fail with `RxCocoaObjCRuntimeError`.
- returns: Observable sequence of object deallocating events.
*/
public var deallocating: Observable<()> {
return self.synchronized {
do {
let proxy: DeallocatingProxy = try self.registerMessageInterceptor(deallocSelector)
return proxy.messageSent.asObservable()
}
catch let e {
return Observable.error(e)
}
}
}
private func registerMessageInterceptor<T: MessageInterceptorSubject>(_ selector: Selector) throws -> T {
let rxSelector = RX_selector(selector)
let selectorReference = RX_reference_from_selector(rxSelector)
let subject: T
if let existingSubject = objc_getAssociatedObject(self.base, selectorReference) as? T {
subject = existingSubject
}
else {
subject = T()
objc_setAssociatedObject(
self.base,
selectorReference,
subject,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
}
if subject.isActive {
return subject
}
var error: NSError?
let targetImplementation = RX_ensure_observing(self.base, selector, &error)
if targetImplementation == nil {
throw error?.rxCocoaErrorForTarget(self.base) ?? RxCocoaError.unknown
}
subject.targetImplementation = targetImplementation!
return subject
}
#endif
}
// MARK: Message interceptors
#if !DISABLE_SWIZZLING && !os(Linux)
private protocol MessageInterceptorSubject: AnyObject {
init()
var isActive: Bool {
get
}
var targetImplementation: IMP { get set }
}
private final class DeallocatingProxy
: MessageInterceptorSubject
, RXDeallocatingObserver {
typealias Element = ()
let messageSent = ReplaySubject<()>.create(bufferSize: 1)
@objc var targetImplementation: IMP = RX_default_target_implementation()
var isActive: Bool {
return self.targetImplementation != RX_default_target_implementation()
}
init() {
}
@objc func deallocating() {
self.messageSent.on(.next(()))
}
deinit {
self.messageSent.on(.completed)
}
}
private final class MessageSentProxy
: MessageInterceptorSubject
, RXMessageSentObserver {
typealias Element = [AnyObject]
let messageSent = PublishSubject<[Any]>()
let methodInvoked = PublishSubject<[Any]>()
@objc var targetImplementation: IMP = RX_default_target_implementation()
var isActive: Bool {
return self.targetImplementation != RX_default_target_implementation()
}
init() {
}
@objc func messageSent(withArguments arguments: [Any]) {
self.messageSent.on(.next(arguments))
}
@objc func methodInvoked(withArguments arguments: [Any]) {
self.methodInvoked.on(.next(arguments))
}
deinit {
self.messageSent.on(.completed)
self.methodInvoked.on(.completed)
}
}
#endif
private final class DeallocObservable {
let subject = ReplaySubject<Void>.create(bufferSize:1)
init() {
}
deinit {
self.subject.on(.next(()))
self.subject.on(.completed)
}
}
// MARK: KVO
#if !os(Linux)
private protocol KVOObservableProtocol {
var target: AnyObject { get }
var keyPath: String { get }
var retainTarget: Bool { get }
var options: KeyValueObservingOptions { get }
}
private final class KVOObserver
: _RXKVOObserver
, Disposable {
typealias Callback = (Any?) -> Void
var retainSelf: KVOObserver?
init(parent: KVOObservableProtocol, callback: @escaping Callback) {
#if TRACE_RESOURCES
_ = Resources.incrementTotal()
#endif
super.init(target: parent.target, retainTarget: parent.retainTarget, keyPath: parent.keyPath, options: parent.options.nsOptions, callback: callback)
self.retainSelf = self
}
override func dispose() {
super.dispose()
self.retainSelf = nil
}
deinit {
#if TRACE_RESOURCES
_ = Resources.decrementTotal()
#endif
}
}
private final class KVOObservable<Element>
: ObservableType
, KVOObservableProtocol {
typealias Element = Element?
unowned var target: AnyObject
var strongTarget: AnyObject?
var keyPath: String
var options: KeyValueObservingOptions
var retainTarget: Bool
init(object: AnyObject, keyPath: String, options: KeyValueObservingOptions, retainTarget: Bool) {
self.target = object
self.keyPath = keyPath
self.options = options
self.retainTarget = retainTarget
if retainTarget {
self.strongTarget = object
}
}
func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element? {
let observer = KVOObserver(parent: self) { value in
if value as? NSNull != nil {
observer.on(.next(nil))
return
}
observer.on(.next(value as? Element))
}
return Disposables.create(with: observer.dispose)
}
}
private extension KeyValueObservingOptions {
var nsOptions: NSKeyValueObservingOptions {
var result: UInt = 0
if self.contains(.new) {
result |= NSKeyValueObservingOptions.new.rawValue
}
if self.contains(.initial) {
result |= NSKeyValueObservingOptions.initial.rawValue
}
return NSKeyValueObservingOptions(rawValue: result)
}
}
#endif
#if !DISABLE_SWIZZLING && !os(Linux)
private func observeWeaklyKeyPathFor(_ target: NSObject, keyPath: String, options: KeyValueObservingOptions) -> Observable<AnyObject?> {
let components = keyPath.components(separatedBy: ".").filter { $0 != "self" }
let observable = observeWeaklyKeyPathFor(target, keyPathSections: components, options: options)
.finishWithNilWhenDealloc(target)
if !options.isDisjoint(with: .initial) {
return observable
}
else {
return observable
.skip(1)
}
}
// This should work correctly
// Identifiers can't contain `,`, so the only place where `,` can appear
// is as a delimiter.
// This means there is `W` as element in an array of property attributes.
private func isWeakProperty(_ properyRuntimeInfo: String) -> Bool {
properyRuntimeInfo.range(of: ",W,") != nil
}
private extension ObservableType where Element == AnyObject? {
func finishWithNilWhenDealloc(_ target: NSObject)
-> Observable<AnyObject?> {
let deallocating = target.rx.deallocating
return deallocating
.map { _ in
return Observable.just(nil)
}
.startWith(self.asObservable())
.switchLatest()
}
}
private func observeWeaklyKeyPathFor(
_ target: NSObject,
keyPathSections: [String],
options: KeyValueObservingOptions
) -> Observable<AnyObject?> {
weak var weakTarget: AnyObject? = target
let propertyName = keyPathSections[0]
let remainingPaths = Array(keyPathSections[1..<keyPathSections.count])
let property = class_getProperty(object_getClass(target), propertyName)
if property == nil {
return Observable.error(RxCocoaError.invalidPropertyName(object: target, propertyName: propertyName))
}
let propertyAttributes = property_getAttributes(property!)
// should dealloc hook be in place if week property, or just create strong reference because it doesn't matter
let isWeak = isWeakProperty(propertyAttributes.map(String.init) ?? "")
let propertyObservable = KVOObservable(object: target, keyPath: propertyName, options: options.union(.initial), retainTarget: false) as KVOObservable<AnyObject>
// KVO recursion for value changes
return propertyObservable
.flatMapLatest { (nextTarget: AnyObject?) -> Observable<AnyObject?> in
if nextTarget == nil {
return Observable.just(nil)
}
let nextObject = nextTarget! as? NSObject
let strongTarget: AnyObject? = weakTarget
if nextObject == nil {
return Observable.error(RxCocoaError.invalidObjectOnKeyPath(object: nextTarget!, sourceObject: strongTarget ?? NSNull(), propertyName: propertyName))
}
// if target is alive, then send change
// if it's deallocated, don't send anything
if strongTarget == nil {
return Observable.empty()
}
let nextElementsObservable = keyPathSections.count == 1
? Observable.just(nextTarget)
: observeWeaklyKeyPathFor(nextObject!, keyPathSections: remainingPaths, options: options)
if isWeak {
return nextElementsObservable
.finishWithNilWhenDealloc(nextObject!)
}
else {
return nextElementsObservable
}
}
}
#endif
// MARK: Constants
private let deallocSelector = NSSelectorFromString("dealloc")
// MARK: AnyObject + Reactive
extension Reactive where Base: AnyObject {
func synchronized<T>( _ action: () -> T) -> T {
objc_sync_enter(self.base)
let result = action()
objc_sync_exit(self.base)
return result
}
}
extension Reactive where Base: AnyObject {
/**
Helper to make sure that `Observable` returned from `createCachedObservable` is only created once.
This is important because there is only one `target` and `action` properties on `NSControl` or `UIBarButtonItem`.
*/
func lazyInstanceObservable<T: AnyObject>(_ key: UnsafeRawPointer, createCachedObservable: () -> T) -> T {
if let value = objc_getAssociatedObject(self.base, key) {
return value as! T
}
let observable = createCachedObservable()
objc_setAssociatedObject(self.base, key, observable, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return observable
}
}
#endif

View File

@@ -0,0 +1,31 @@
//
// NotificationCenter+Rx.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 5/2/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation
import RxSwift
extension Reactive where Base: NotificationCenter {
/**
Transforms notifications posted to notification center to observable sequence of notifications.
- parameter name: Optional name used to filter notifications.
- parameter object: Optional object used to filter notifications.
- returns: Observable sequence of posted notifications.
*/
public func notification(_ name: Notification.Name?, object: AnyObject? = nil) -> Observable<Notification> {
return Observable.create { [weak object] observer in
let nsObserver = self.base.addObserver(forName: name, object: object, queue: nil) { notification in
observer.on(.next(notification))
}
return Disposables.create {
self.base.removeObserver(nsObserver)
}
}
}
}

View File

@@ -0,0 +1,240 @@
//
// URLSession+Rx.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 3/23/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation
import RxSwift
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
/// RxCocoa URL errors.
public enum RxCocoaURLError
: Swift.Error {
/// Unknown error occurred.
case unknown
/// Response is not NSHTTPURLResponse
case nonHTTPResponse(response: URLResponse)
/// Response is not successful. (not in `200 ..< 300` range)
case httpRequestFailed(response: HTTPURLResponse, data: Data?)
/// Deserialization error.
case deserializationError(error: Swift.Error)
}
extension RxCocoaURLError
: CustomDebugStringConvertible {
/// A textual representation of `self`, suitable for debugging.
public var debugDescription: String {
switch self {
case .unknown:
return "Unknown error has occurred."
case let .nonHTTPResponse(response):
return "Response is not NSHTTPURLResponse `\(response)`."
case let .httpRequestFailed(response, _):
return "HTTP request failed with `\(response.statusCode)`."
case let .deserializationError(error):
return "Error during deserialization of the response: \(error)"
}
}
}
private func escapeTerminalString(_ value: String) -> String {
return value.replacingOccurrences(of: "\"", with: "\\\"", options:[], range: nil)
}
private func convertURLRequestToCurlCommand(_ request: URLRequest) -> String {
let method = request.httpMethod ?? "GET"
var returnValue = "curl -X \(method) "
if let httpBody = request.httpBody {
let maybeBody = String(data: httpBody, encoding: String.Encoding.utf8)
if let body = maybeBody {
returnValue += "-d \"\(escapeTerminalString(body))\" "
}
}
for (key, value) in request.allHTTPHeaderFields ?? [:] {
let escapedKey = escapeTerminalString(key as String)
let escapedValue = escapeTerminalString(value as String)
returnValue += "\n -H \"\(escapedKey): \(escapedValue)\" "
}
let URLString = request.url?.absoluteString ?? "<unknown url>"
returnValue += "\n\"\(escapeTerminalString(URLString))\""
returnValue += " -i -v"
return returnValue
}
private func convertResponseToString(_ response: URLResponse?, _ error: NSError?, _ interval: TimeInterval) -> String {
let ms = Int(interval * 1000)
if let response = response as? HTTPURLResponse {
if 200 ..< 300 ~= response.statusCode {
return "Success (\(ms)ms): Status \(response.statusCode)"
}
else {
return "Failure (\(ms)ms): Status \(response.statusCode)"
}
}
if let error = error {
if error.domain == NSURLErrorDomain && error.code == NSURLErrorCancelled {
return "Canceled (\(ms)ms)"
}
return "Failure (\(ms)ms): NSError > \(error)"
}
return "<Unhandled response from server>"
}
extension Reactive where Base: URLSession {
/**
Observable sequence of responses for URL request.
Performing of request starts after observer is subscribed and not after invoking this method.
**URL requests will be performed per subscribed observer.**
Any error during fetching of the response will cause observed sequence to terminate with error.
- parameter request: URL request.
- returns: Observable sequence of URL responses.
*/
public func response(request: URLRequest) -> Observable<(response: HTTPURLResponse, data: Data)> {
return Observable.create { observer in
// smart compiler should be able to optimize this out
let d: Date?
if URLSession.rx.shouldLogRequest(request) {
d = Date()
}
else {
d = nil
}
let task = self.base.dataTask(with: request) { data, response, error in
if URLSession.rx.shouldLogRequest(request) {
let interval = Date().timeIntervalSince(d ?? Date())
print(convertURLRequestToCurlCommand(request))
#if os(Linux)
print(convertResponseToString(response, error.flatMap { $0 as NSError }, interval))
#else
print(convertResponseToString(response, error.map { $0 as NSError }, interval))
#endif
}
guard let response = response, let data = data else {
observer.on(.error(error ?? RxCocoaURLError.unknown))
return
}
guard let httpResponse = response as? HTTPURLResponse else {
observer.on(.error(RxCocoaURLError.nonHTTPResponse(response: response)))
return
}
observer.on(.next((httpResponse, data)))
observer.on(.completed)
}
task.resume()
return Disposables.create(with: task.cancel)
}
}
/**
Observable sequence of response data for URL request.
Performing of request starts after observer is subscribed and not after invoking this method.
**URL requests will be performed per subscribed observer.**
Any error during fetching of the response will cause observed sequence to terminate with error.
If response is not HTTP response with status code in the range of `200 ..< 300`, sequence
will terminate with `(RxCocoaErrorDomain, RxCocoaError.NetworkError)`.
- parameter request: URL request.
- returns: Observable sequence of response data.
*/
public func data(request: URLRequest) -> Observable<Data> {
return self.response(request: request).map { pair -> Data in
if 200 ..< 300 ~= pair.0.statusCode {
return pair.1
}
else {
throw RxCocoaURLError.httpRequestFailed(response: pair.0, data: pair.1)
}
}
}
/**
Observable sequence of response JSON for URL request.
Performing of request starts after observer is subscribed and not after invoking this method.
**URL requests will be performed per subscribed observer.**
Any error during fetching of the response will cause observed sequence to terminate with error.
If response is not HTTP response with status code in the range of `200 ..< 300`, sequence
will terminate with `(RxCocoaErrorDomain, RxCocoaError.NetworkError)`.
If there is an error during JSON deserialization observable sequence will fail with that error.
- parameter request: URL request.
- returns: Observable sequence of response JSON.
*/
public func json(request: URLRequest, options: JSONSerialization.ReadingOptions = []) -> Observable<Any> {
return self.data(request: request).map { data -> Any in
do {
return try JSONSerialization.jsonObject(with: data, options: options)
} catch let error {
throw RxCocoaURLError.deserializationError(error: error)
}
}
}
/**
Observable sequence of response JSON for GET request with `URL`.
Performing of request starts after observer is subscribed and not after invoking this method.
**URL requests will be performed per subscribed observer.**
Any error during fetching of the response will cause observed sequence to terminate with error.
If response is not HTTP response with status code in the range of `200 ..< 300`, sequence
will terminate with `(RxCocoaErrorDomain, RxCocoaError.NetworkError)`.
If there is an error during JSON deserialization observable sequence will fail with that error.
- parameter url: URL of `NSURLRequest` request.
- returns: Observable sequence of response JSON.
*/
public func json(url: Foundation.URL) -> Observable<Any> {
self.json(request: URLRequest(url: url))
}
}
extension Reactive where Base == URLSession {
/// Log URL requests to standard output in curl format.
public static var shouldLogRequest: (URLRequest) -> Bool = { _ in
#if DEBUG
return true
#else
return false
#endif
}
}

10
Pods/RxCocoa/RxCocoa/Runtime/_RX.m generated Normal file
View File

@@ -0,0 +1,10 @@
//
// _RX.m
// RxCocoa
//
// Created by Krunoslav Zaher on 7/12/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#import "include/_RX.h"

View File

@@ -0,0 +1,147 @@
//
// _RXDelegateProxy.m
// RxCocoa
//
// Created by Krunoslav Zaher on 7/4/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#import "include/_RXDelegateProxy.h"
#import "include/_RX.h"
#import "include/_RXObjCRuntime.h"
@interface _RXDelegateProxy () {
id __weak __forwardToDelegate;
}
@property (nonatomic, strong) id strongForwardDelegate;
@end
static NSMutableDictionary *voidSelectorsPerClass = nil;
@implementation _RXDelegateProxy
+(NSSet*)collectVoidSelectorsForProtocol:(Protocol *)protocol {
NSMutableSet *selectors = [NSMutableSet set];
unsigned int protocolMethodCount = 0;
struct objc_method_description *pMethods = protocol_copyMethodDescriptionList(protocol, NO, YES, &protocolMethodCount);
for (unsigned int i = 0; i < protocolMethodCount; ++i) {
struct objc_method_description method = pMethods[i];
if (RX_is_method_with_description_void(method)) {
[selectors addObject:SEL_VALUE(method.name)];
}
}
free(pMethods);
unsigned int numberOfBaseProtocols = 0;
Protocol * __unsafe_unretained * pSubprotocols = protocol_copyProtocolList(protocol, &numberOfBaseProtocols);
for (unsigned int i = 0; i < numberOfBaseProtocols; ++i) {
[selectors unionSet:[self collectVoidSelectorsForProtocol:pSubprotocols[i]]];
}
free(pSubprotocols);
return selectors;
}
+(void)initialize {
@synchronized (_RXDelegateProxy.class) {
if (voidSelectorsPerClass == nil) {
voidSelectorsPerClass = [[NSMutableDictionary alloc] init];
}
NSMutableSet *voidSelectors = [NSMutableSet set];
#define CLASS_HIERARCHY_MAX_DEPTH 100
NSInteger classHierarchyDepth = 0;
Class targetClass = NULL;
for (classHierarchyDepth = 0, targetClass = self;
classHierarchyDepth < CLASS_HIERARCHY_MAX_DEPTH && targetClass != nil;
++classHierarchyDepth, targetClass = class_getSuperclass(targetClass)
) {
unsigned int count;
Protocol *__unsafe_unretained *pProtocols = class_copyProtocolList(targetClass, &count);
for (unsigned int i = 0; i < count; i++) {
NSSet *selectorsForProtocol = [self collectVoidSelectorsForProtocol:pProtocols[i]];
[voidSelectors unionSet:selectorsForProtocol];
}
free(pProtocols);
}
if (classHierarchyDepth == CLASS_HIERARCHY_MAX_DEPTH) {
NSLog(@"Detected weird class hierarchy with depth over %d. Starting with this class -> %@", CLASS_HIERARCHY_MAX_DEPTH, self);
#if DEBUG
abort();
#endif
}
voidSelectorsPerClass[CLASS_VALUE(self)] = voidSelectors;
}
}
-(id)_forwardToDelegate {
return __forwardToDelegate;
}
-(void)_setForwardToDelegate:(id __nullable)forwardToDelegate retainDelegate:(BOOL)retainDelegate {
__forwardToDelegate = forwardToDelegate;
if (retainDelegate) {
self.strongForwardDelegate = forwardToDelegate;
}
else {
self.strongForwardDelegate = nil;
}
}
-(BOOL)hasWiredImplementationForSelector:(SEL)selector {
return [super respondsToSelector:selector];
}
-(BOOL)voidDelegateMethodsContain:(SEL)selector {
@synchronized(_RXDelegateProxy.class) {
NSSet *voidSelectors = voidSelectorsPerClass[CLASS_VALUE(self.class)];
NSAssert(voidSelectors != nil, @"Set of allowed methods not initialized");
return [voidSelectors containsObject:SEL_VALUE(selector)];
}
}
-(void)forwardInvocation:(NSInvocation *)anInvocation {
BOOL isVoid = RX_is_method_signature_void(anInvocation.methodSignature);
NSArray *arguments = nil;
if (isVoid) {
arguments = RX_extract_arguments(anInvocation);
[self _sentMessage:anInvocation.selector withArguments:arguments];
}
if (self._forwardToDelegate && [self._forwardToDelegate respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:self._forwardToDelegate];
}
if (isVoid) {
[self _methodInvoked:anInvocation.selector withArguments:arguments];
}
}
// abstract method
-(void)_sentMessage:(SEL)selector withArguments:(NSArray *)arguments {
}
// abstract method
-(void)_methodInvoked:(SEL)selector withArguments:(NSArray *)arguments {
}
-(void)dealloc {
}
@end

View File

@@ -0,0 +1,54 @@
//
// _RXKVOObserver.m
// RxCocoa
//
// Created by Krunoslav Zaher on 7/11/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#import "include/_RXKVOObserver.h"
@interface _RXKVOObserver ()
@property (nonatomic, unsafe_unretained) id target;
@property (nonatomic, strong ) id retainedTarget;
@property (nonatomic, copy ) NSString *keyPath;
@property (nonatomic, copy ) void (^callback)(id);
@end
@implementation _RXKVOObserver
-(instancetype)initWithTarget:(id)target
retainTarget:(BOOL)retainTarget
keyPath:(NSString*)keyPath
options:(NSKeyValueObservingOptions)options
callback:(void (^)(id))callback {
self = [super init];
if (!self) return nil;
self.target = target;
if (retainTarget) {
self.retainedTarget = target;
}
self.keyPath = keyPath;
self.callback = callback;
[self.target addObserver:self forKeyPath:self.keyPath options:options context:nil];
return self;
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
@synchronized(self) {
self.callback(change[NSKeyValueChangeNewKey]);
}
}
-(void)dispose {
[self.target removeObserver:self forKeyPath:self.keyPath context:nil];
self.target = nil;
self.retainedTarget = nil;
}
@end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
//
// RxCocoaRuntime.h
// RxCocoa
//
// Created by Krunoslav Zaher on 2/21/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "_RX.h"
#import "_RXDelegateProxy.h"
#import "_RXKVOObserver.h"
#import "_RXObjCRuntime.h"
//! Project version number for RxCocoa.
FOUNDATION_EXPORT double RxCocoaVersionNumber;
//! Project version string for RxCocoa.
FOUNDATION_EXPORT const unsigned char RxCocoaVersionString[];

View File

@@ -0,0 +1,93 @@
//
// _RX.h
// RxCocoa
//
// Created by Krunoslav Zaher on 7/12/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
/**
################################################################################
This file is part of RX private API
################################################################################
*/
#if TRACE_RESOURCES >= 2
# define DLOG(...) NSLog(__VA_ARGS__)
#else
# define DLOG(...)
#endif
#if DEBUG
# define ABORT_IN_DEBUG abort();
#else
# define ABORT_IN_DEBUG
#endif
#define SEL_VALUE(x) [NSValue valueWithPointer:(x)]
#define CLASS_VALUE(x) [NSValue valueWithNonretainedObject:(x)]
#define IMP_VALUE(x) [NSValue valueWithPointer:(x)]
/**
Checks that the local `error` instance exists before assigning it's value by reference.
This macro exists to work around static analysis warnings — `NSError` is always assumed to be `nullable`, even though we explicitly define the method parameter as `nonnull`. See http://www.openradar.me/21766176 for more details.
*/
#define RX_THROW_ERROR(errorValue, returnValue) if (error != nil) { *error = (errorValue); } return (returnValue);
#define RX_CAT2(_1, _2) _RX_CAT2(_1, _2)
#define _RX_CAT2(_1, _2) _1 ## _2
#define RX_ELEMENT_AT(n, ...) RX_CAT2(_RX_ELEMENT_AT_, n)(__VA_ARGS__)
#define _RX_ELEMENT_AT_0(x, ...) x
#define _RX_ELEMENT_AT_1(_0, x, ...) x
#define _RX_ELEMENT_AT_2(_0, _1, x, ...) x
#define _RX_ELEMENT_AT_3(_0, _1, _2, x, ...) x
#define _RX_ELEMENT_AT_4(_0, _1, _2, _3, x, ...) x
#define _RX_ELEMENT_AT_5(_0, _1, _2, _3, _4, x, ...) x
#define _RX_ELEMENT_AT_6(_0, _1, _2, _3, _4, _5, x, ...) x
#define RX_COUNT(...) RX_ELEMENT_AT(6, ## __VA_ARGS__, 6, 5, 4, 3, 2, 1, 0)
#define RX_EMPTY(...) RX_ELEMENT_AT(6, ## __VA_ARGS__, 0, 0, 0, 0, 0, 0, 1)
/**
#define SUM(context, index, head, tail) head + tail
#define MAP(context, index, element) (context)[index] * (element)
RX_FOR(numbers, SUM, MAP, b0, b1, b2);
(numbers)[0] * (b0) + (numbers)[1] * (b1) + (numbers[2]) * (b2)
*/
#define RX_FOREACH(context, concat, map, ...) RX_FOR_MAX(RX_COUNT(__VA_ARGS__), _RX_FOREACH_CONCAT, _RX_FOREACH_MAP, context, concat, map, __VA_ARGS__)
#define _RX_FOREACH_CONCAT(index, head, tail, context, concat, map, ...) concat(context, index, head, tail)
#define _RX_FOREACH_MAP(index, context, concat, map, ...) map(context, index, RX_ELEMENT_AT(index, __VA_ARGS__))
/**
#define MAP(context, index, item) (context)[index] * (item)
RX_FOR_COMMA(numbers, MAP, b0, b1);
,(numbers)[0] * b0, (numbers)[1] * b1
*/
#define RX_FOREACH_COMMA(context, map, ...) RX_CAT2(_RX_FOREACH_COMMA_EMPTY_, RX_EMPTY(__VA_ARGS__))(context, map, ## __VA_ARGS__)
#define _RX_FOREACH_COMMA_EMPTY_1(context, map, ...)
#define _RX_FOREACH_COMMA_EMPTY_0(context, map, ...) , RX_FOR_MAX(RX_COUNT(__VA_ARGS__), _RX_FOREACH_COMMA_CONCAT, _RX_FOREACH_COMMA_MAP, context, map, __VA_ARGS__)
#define _RX_FOREACH_COMMA_CONCAT(index, head, tail, context, map, ...) head, tail
#define _RX_FOREACH_COMMA_MAP(index, context, map, ...) map(context, index, RX_ELEMENT_AT(index, __VA_ARGS__))
// rx for
#define RX_FOR_MAX(max, concat, map, ...) RX_CAT2(RX_FOR_, max)(concat, map, ## __VA_ARGS__)
#define RX_FOR_0(concat, map, ...)
#define RX_FOR_1(concat, map, ...) map(0, __VA_ARGS__)
#define RX_FOR_2(concat, map, ...) concat(1, RX_FOR_1(concat, map, ## __VA_ARGS__), map(1, __VA_ARGS__), __VA_ARGS__)
#define RX_FOR_3(concat, map, ...) concat(2, RX_FOR_2(concat, map, ## __VA_ARGS__), map(2, __VA_ARGS__), __VA_ARGS__)
#define RX_FOR_4(concat, map, ...) concat(3, RX_FOR_3(concat, map, ## __VA_ARGS__), map(3, __VA_ARGS__), __VA_ARGS__)
#define RX_FOR_5(concat, map, ...) concat(4, RX_FOR_4(concat, map, ## __VA_ARGS__), map(4, __VA_ARGS__), __VA_ARGS__)
#define RX_FOR_6(concat, map, ...) concat(5, RX_FOR_5(concat, map, ## __VA_ARGS__), map(5, __VA_ARGS__), __VA_ARGS__)

View File

@@ -0,0 +1,27 @@
//
// _RXDelegateProxy.h
// RxCocoa
//
// Created by Krunoslav Zaher on 7/4/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface _RXDelegateProxy : NSObject
@property (nonatomic, weak, readonly) id _forwardToDelegate;
-(void)_setForwardToDelegate:(id __nullable)forwardToDelegate retainDelegate:(BOOL)retainDelegate NS_SWIFT_NAME(_setForwardToDelegate(_:retainDelegate:)) ;
-(BOOL)hasWiredImplementationForSelector:(SEL)selector;
-(BOOL)voidDelegateMethodsContain:(SEL)selector;
-(void)_sentMessage:(SEL)selector withArguments:(NSArray*)arguments;
-(void)_methodInvoked:(SEL)selector withArguments:(NSArray*)arguments;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,28 @@
//
// _RXKVOObserver.h
// RxCocoa
//
// Created by Krunoslav Zaher on 7/11/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
################################################################################
This file is part of RX private API
################################################################################
*/
// Exists because if written in Swift, reading unowned is disabled during dealloc process
@interface _RXKVOObserver : NSObject
-(instancetype)initWithTarget:(id)target
retainTarget:(BOOL)retainTarget
keyPath:(NSString*)keyPath
options:(NSKeyValueObservingOptions)options
callback:(void (^)(id))callback;
-(void)dispose;
@end

View File

@@ -0,0 +1,102 @@
//
// _RXObjCRuntime.h
// RxCocoa
//
// Created by Krunoslav Zaher on 7/11/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#import <Foundation/Foundation.h>
#if !DISABLE_SWIZZLING
/**
################################################################################
This file is part of RX private API
################################################################################
*/
/**
This flag controls `RELEASE` configuration behavior in case race was detecting while modifying
ObjC runtime.
In case this value is set to `YES`, after runtime race is detected, `abort()` will be called.
Otherwise, only error will be reported using normal error reporting mechanism.
In `DEBUG` mode `abort` will be always called in case race is detected.
Races can't happen in case this is the only library modifying ObjC runtime, but in case there are multiple libraries
changing ObjC runtime, race conditions can occur because there is no way to synchronize multiple libraries unaware of
each other.
To help remedy this situation this library will use `synchronized` on target object and it's meta-class, but
there aren't any guarantees of how other libraries will behave.
Default value is `NO`.
*/
extern BOOL RXAbortOnThreadingHazard;
/// Error domain for RXObjCRuntime.
extern NSString * __nonnull const RXObjCRuntimeErrorDomain;
/// `userInfo` key with additional information is interceptor probably KVO.
extern NSString * __nonnull const RXObjCRuntimeErrorIsKVOKey;
typedef NS_ENUM(NSInteger, RXObjCRuntimeError) {
RXObjCRuntimeErrorUnknown = 1,
RXObjCRuntimeErrorObjectMessagesAlreadyBeingIntercepted = 2,
RXObjCRuntimeErrorSelectorNotImplemented = 3,
RXObjCRuntimeErrorCantInterceptCoreFoundationTollFreeBridgedObjects = 4,
RXObjCRuntimeErrorThreadingCollisionWithOtherInterceptionMechanism = 5,
RXObjCRuntimeErrorSavingOriginalForwardingMethodFailed = 6,
RXObjCRuntimeErrorReplacingMethodWithForwardingImplementation = 7,
RXObjCRuntimeErrorObservingPerformanceSensitiveMessages = 8,
RXObjCRuntimeErrorObservingMessagesWithUnsupportedReturnType = 9,
};
/// Transforms normal selector into a selector with RX prefix.
SEL _Nonnull RX_selector(SEL _Nonnull selector);
/// Transforms selector into a unique pointer (because of Swift conversion rules)
void * __nonnull RX_reference_from_selector(SEL __nonnull selector);
/// Protocol that interception observers must implement.
@protocol RXMessageSentObserver
/// In case the same selector is being intercepted for a pair of base/sub classes,
/// this property will differentiate between interceptors that need to fire.
@property (nonatomic, assign, readonly) IMP __nonnull targetImplementation;
-(void)messageSentWithArguments:(NSArray* __nonnull)arguments;
-(void)methodInvokedWithArguments:(NSArray* __nonnull)arguments;
@end
/// Protocol that deallocating observer must implement.
@protocol RXDeallocatingObserver
/// In case the same selector is being intercepted for a pair of base/sub classes,
/// this property will differentiate between interceptors that need to fire.
@property (nonatomic, assign, readonly) IMP __nonnull targetImplementation;
-(void)deallocating;
@end
/// Ensures interceptor is installed on target object.
IMP __nullable RX_ensure_observing(id __nonnull target, SEL __nonnull selector, NSError *__autoreleasing __nullable * __nullable error);
#endif
/// Extracts arguments for `invocation`.
NSArray * __nonnull RX_extract_arguments(NSInvocation * __nonnull invocation);
/// Returns `YES` in case method has `void` return type.
BOOL RX_is_method_with_description_void(struct objc_method_description method);
/// Returns `YES` in case methodSignature has `void` return type.
BOOL RX_is_method_signature_void(NSMethodSignature * __nonnull methodSignature);
/// Default value for `RXInterceptionObserver.targetImplementation`.
IMP __nonnull RX_default_target_implementation(void);

19
Pods/RxCocoa/RxCocoa/RxCocoa.h generated Normal file
View File

@@ -0,0 +1,19 @@
//
// RxCocoa.h
// RxCocoa
//
// Created by Krunoslav Zaher on 2/21/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <RxCocoa/_RX.h>
#import <RxCocoa/_RXDelegateProxy.h>
#import <RxCocoa/_RXKVOObserver.h>
#import <RxCocoa/_RXObjCRuntime.h>
//! Project version number for RxCocoa.
FOUNDATION_EXPORT double RxCocoaVersionNumber;
//! Project version string for RxCocoa.
FOUNDATION_EXPORT const unsigned char RxCocoaVersionString[];

155
Pods/RxCocoa/RxCocoa/RxCocoa.swift generated Normal file
View File

@@ -0,0 +1,155 @@
//
// RxCocoa.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 2/21/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation
// Importing RxCocoa also imports RxRelay
@_exported import RxRelay
import RxSwift
#if os(iOS)
import UIKit
#endif
/// RxCocoa errors.
public enum RxCocoaError
: Swift.Error
, CustomDebugStringConvertible {
/// Unknown error has occurred.
case unknown
/// Invalid operation was attempted.
case invalidOperation(object: Any)
/// Items are not yet bound to user interface but have been requested.
case itemsNotYetBound(object: Any)
/// Invalid KVO Path.
case invalidPropertyName(object: Any, propertyName: String)
/// Invalid object on key path.
case invalidObjectOnKeyPath(object: Any, sourceObject: AnyObject, propertyName: String)
/// Error during swizzling.
case errorDuringSwizzling
/// Casting error.
case castingError(object: Any, targetType: Any.Type)
}
// MARK: Debug descriptions
extension RxCocoaError {
/// A textual representation of `self`, suitable for debugging.
public var debugDescription: String {
switch self {
case .unknown:
return "Unknown error occurred."
case let .invalidOperation(object):
return "Invalid operation was attempted on `\(object)`."
case let .itemsNotYetBound(object):
return "Data source is set, but items are not yet bound to user interface for `\(object)`."
case let .invalidPropertyName(object, propertyName):
return "Object `\(object)` doesn't have a property named `\(propertyName)`."
case let .invalidObjectOnKeyPath(object, sourceObject, propertyName):
return "Unobservable object `\(object)` was observed as `\(propertyName)` of `\(sourceObject)`."
case .errorDuringSwizzling:
return "Error during swizzling."
case let .castingError(object, targetType):
return "Error casting `\(object)` to `\(targetType)`"
}
}
}
// MARK: Error binding policies
func bindingError(_ error: Swift.Error) {
let error = "Binding error: \(error)"
#if DEBUG
rxFatalError(error)
#else
print(error)
#endif
}
/// Swift does not implement abstract methods. This method is used as a runtime check to ensure that methods which intended to be abstract (i.e., they should be implemented in subclasses) are not called directly on the superclass.
func rxAbstractMethod(message: String = "Abstract method", file: StaticString = #file, line: UInt = #line) -> Swift.Never {
rxFatalError(message, file: file, line: line)
}
func rxFatalError(_ lastMessage: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line) -> Swift.Never {
// The temptation to comment this line is great, but please don't, it's for your own good. The choice is yours.
fatalError(lastMessage(), file: file, line: line)
}
func rxFatalErrorInDebug(_ lastMessage: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line) {
#if DEBUG
fatalError(lastMessage(), file: file, line: line)
#else
print("\(file):\(line): \(lastMessage())")
#endif
}
// MARK: casts or fatal error
// workaround for Swift compiler bug, cheers compiler team :)
func castOptionalOrFatalError<T>(_ value: Any?) -> T? {
if value == nil {
return nil
}
let v: T = castOrFatalError(value)
return v
}
func castOrThrow<T>(_ resultType: T.Type, _ object: Any) throws -> T {
guard let returnValue = object as? T else {
throw RxCocoaError.castingError(object: object, targetType: resultType)
}
return returnValue
}
func castOptionalOrThrow<T>(_ resultType: T.Type, _ object: AnyObject) throws -> T? {
if NSNull().isEqual(object) {
return nil
}
guard let returnValue = object as? T else {
throw RxCocoaError.castingError(object: object, targetType: resultType)
}
return returnValue
}
func castOrFatalError<T>(_ value: AnyObject!, message: String) -> T {
let maybeResult: T? = value as? T
guard let result = maybeResult else {
rxFatalError(message)
}
return result
}
func castOrFatalError<T>(_ value: Any!) -> T {
let maybeResult: T? = value as? T
guard let result = maybeResult else {
rxFatalError("Failure converting from \(String(describing: value)) to \(T.self)")
}
return result
}
// MARK: Error messages
let dataSourceNotSet = "DataSource not set"
let delegateNotSet = "Delegate not set"
// MARK: Shared with RxSwift
func rxFatalError(_ lastMessage: String) -> Never {
// The temptation to comment this line is great, but please don't, it's for your own good. The choice is yours.
fatalError(lastMessage)
}

View File

@@ -0,0 +1,68 @@
//
// ControlEvent.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 8/28/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import RxSwift
/// A protocol that extends `ControlEvent`.
public protocol ControlEventType : ObservableType {
/// - returns: `ControlEvent` interface
func asControlEvent() -> ControlEvent<Element>
}
/**
A trait for `Observable`/`ObservableType` that represents an event on a UI element.
Properties:
- it doesnt send any initial value on subscription,
- it `Complete`s the sequence when the control deallocates,
- it never errors out
- it delivers events on `MainScheduler.instance`.
**The implementation of `ControlEvent` will ensure that sequence of events is being subscribed on main scheduler
(`subscribe(on: ConcurrentMainScheduler.instance)` behavior).**
**It is the implementors responsibility to make sure that all other properties enumerated above are satisfied.**
**If they arent, using this trait will communicate wrong properties, and could potentially break someones code.**
**If the `events` observable sequence passed into the initializer doesnt satisfy all enumerated
properties, dont use this trait.**
*/
public struct ControlEvent<PropertyType> : ControlEventType {
public typealias Element = PropertyType
let events: Observable<PropertyType>
/// Initializes control event with a observable sequence that represents events.
///
/// - parameter events: Observable sequence that represents events.
/// - returns: Control event created with a observable sequence of events.
public init<Ev: ObservableType>(events: Ev) where Ev.Element == Element {
self.events = events.subscribe(on: ConcurrentMainScheduler.instance)
}
/// Subscribes an observer to control events.
///
/// - parameter observer: Observer to subscribe to events.
/// - returns: Disposable object that can be used to unsubscribe the observer from receiving control events.
public func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
self.events.subscribe(observer)
}
/// - returns: `Observable` interface.
public func asObservable() -> Observable<Element> {
self.events
}
/// - returns: `ControlEvent` interface.
public func asControlEvent() -> ControlEvent<Element> {
self
}
}

View File

@@ -0,0 +1,118 @@
//
// ControlProperty.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 8/28/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import RxSwift
/// Protocol that enables extension of `ControlProperty`.
public protocol ControlPropertyType : ObservableType, ObserverType {
/// - returns: `ControlProperty` interface
func asControlProperty() -> ControlProperty<Element>
}
/**
Trait for `Observable`/`ObservableType` that represents property of UI element.
Sequence of values only represents initial control value and user initiated value changes.
Programmatic value changes won't be reported.
It's properties are:
- `shareReplay(1)` behavior
- it's stateful, upon subscription (calling subscribe) last element is immediately replayed if it was produced
- it will `Complete` sequence on control being deallocated
- it never errors out
- it delivers events on `MainScheduler.instance`
**The implementation of `ControlProperty` will ensure that sequence of values is being subscribed on main scheduler
(`subscribe(on: ConcurrentMainScheduler.instance)` behavior).**
**It is implementor's responsibility to make sure that that all other properties enumerated above are satisfied.**
**If they aren't, then using this trait communicates wrong properties and could potentially break someone's code.**
**In case `values` observable sequence that is being passed into initializer doesn't satisfy all enumerated
properties, please don't use this trait.**
*/
public struct ControlProperty<PropertyType> : ControlPropertyType {
public typealias Element = PropertyType
let values: Observable<PropertyType>
let valueSink: AnyObserver<PropertyType>
/// Initializes control property with a observable sequence that represents property values and observer that enables
/// binding values to property.
///
/// - parameter values: Observable sequence that represents property values.
/// - parameter valueSink: Observer that enables binding values to control property.
/// - returns: Control property created with a observable sequence of values and an observer that enables binding values
/// to property.
public init<Values: ObservableType, Sink: ObserverType>(values: Values, valueSink: Sink) where Element == Values.Element, Element == Sink.Element {
self.values = values.subscribe(on: ConcurrentMainScheduler.instance)
self.valueSink = valueSink.asObserver()
}
/// Subscribes an observer to control property values.
///
/// - parameter observer: Observer to subscribe to property values.
/// - returns: Disposable object that can be used to unsubscribe the observer from receiving control property values.
public func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
self.values.subscribe(observer)
}
/// `ControlEvent` of user initiated value changes. Every time user updates control value change event
/// will be emitted from `changed` event.
///
/// Programmatic changes to control value won't be reported.
///
/// It contains all control property values except for first one.
///
/// The name only implies that sequence element will be generated once user changes a value and not that
/// adjacent sequence values need to be different (e.g. because of interaction between programmatic and user updates,
/// or for any other reason).
public var changed: ControlEvent<PropertyType> {
ControlEvent(events: self.values.skip(1))
}
/// - returns: `Observable` interface.
public func asObservable() -> Observable<Element> {
self.values
}
/// - returns: `ControlProperty` interface.
public func asControlProperty() -> ControlProperty<Element> {
self
}
/// Binds event to user interface.
///
/// - In case next element is received, it is being set to control value.
/// - In case error is received, DEBUG builds raise fatal error, RELEASE builds log event to standard output.
/// - In case sequence completes, nothing happens.
public func on(_ event: Event<Element>) {
switch event {
case .error(let error):
bindingError(error)
case .next:
self.valueSink.on(event)
case .completed:
self.valueSink.on(event)
}
}
}
extension ControlPropertyType where Element == String? {
/// Transforms control property of type `String?` into control property of type `String`.
public var orEmpty: ControlProperty<String> {
let original: ControlProperty<String?> = self.asControlProperty()
let values: Observable<String> = original.values.map { $0 ?? "" }
let valueSink: AnyObserver<String> = original.valueSink.mapObserver { $0 }
return ControlProperty<String>(values: values, valueSink: valueSink)
}
}

View File

@@ -0,0 +1,21 @@
//
// BehaviorRelay+Driver.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 10/7/17.
// Copyright © 2017 Krunoslav Zaher. All rights reserved.
//
import RxSwift
import RxRelay
extension BehaviorRelay {
/// Converts `BehaviorRelay` to `Driver`.
///
/// - returns: Observable sequence.
public func asDriver() -> Driver<Element> {
let source = self.asObservable()
.observe(on:DriverSharingStrategy.scheduler)
return SharedSequence(source)
}
}

View File

@@ -0,0 +1,24 @@
//
// ControlEvent+Driver.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 9/19/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import RxSwift
extension ControlEvent {
/// Converts `ControlEvent` to `Driver` trait.
///
/// `ControlEvent` already can't fail, so no special case needs to be handled.
public func asDriver() -> Driver<Element> {
return self.asDriver { _ -> Driver<Element> in
#if DEBUG
rxFatalError("Somehow driver received error from a source that shouldn't fail.")
#else
return Driver.empty()
#endif
}
}
}

View File

@@ -0,0 +1,24 @@
//
// ControlProperty+Driver.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 9/19/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import RxSwift
extension ControlProperty {
/// Converts `ControlProperty` to `Driver` trait.
///
/// `ControlProperty` already can't fail, so no special case needs to be handled.
public func asDriver() -> Driver<Element> {
return self.asDriver { _ -> Driver<Element> in
#if DEBUG
rxFatalError("Somehow driver received error from a source that shouldn't fail.")
#else
return Driver.empty()
#endif
}
}
}

View File

@@ -0,0 +1,203 @@
//
// Driver+Subscription.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 9/19/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import RxSwift
import RxRelay
private let errorMessage = "`drive*` family of methods can be only called from `MainThread`.\n" +
"This is required to ensure that the last replayed `Driver` element is delivered on `MainThread`.\n"
extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingStrategy {
/**
Creates new subscription and sends elements to observer.
This method can be only called from `MainThread`.
In this form it's equivalent to `subscribe` method, but it communicates intent better.
- parameter observers: Observers that receives events.
- returns: Disposable object that can be used to unsubscribe the observer from the subject.
*/
public func drive<Observer: ObserverType>(_ observers: Observer...) -> Disposable where Observer.Element == Element {
MainScheduler.ensureRunningOnMainThread(errorMessage: errorMessage)
return self.asSharedSequence()
.asObservable()
.subscribe { e in
observers.forEach { $0.on(e) }
}
}
/**
Creates new subscription and sends elements to observer.
This method can be only called from `MainThread`.
In this form it's equivalent to `subscribe` method, but it communicates intent better.
- parameter observers: Observers that receives events.
- returns: Disposable object that can be used to unsubscribe the observer from the subject.
*/
public func drive<Observer: ObserverType>(_ observers: Observer...) -> Disposable where Observer.Element == Element? {
MainScheduler.ensureRunningOnMainThread(errorMessage: errorMessage)
return self.asSharedSequence()
.asObservable()
.map { $0 as Element? }
.subscribe { e in
observers.forEach { $0.on(e) }
}
}
/**
Creates new subscription and sends elements to `BehaviorRelay`.
This method can be only called from `MainThread`.
- parameter relays: Target relays for sequence elements.
- returns: Disposable object that can be used to unsubscribe the observer from the relay.
*/
public func drive(_ relays: BehaviorRelay<Element>...) -> Disposable {
MainScheduler.ensureRunningOnMainThread(errorMessage: errorMessage)
return self.drive(onNext: { e in
relays.forEach { $0.accept(e) }
})
}
/**
Creates new subscription and sends elements to `BehaviorRelay`.
This method can be only called from `MainThread`.
- parameter relays: Target relays for sequence elements.
- returns: Disposable object that can be used to unsubscribe the observer from the relay.
*/
public func drive(_ relays: BehaviorRelay<Element?>...) -> Disposable {
MainScheduler.ensureRunningOnMainThread(errorMessage: errorMessage)
return self.drive(onNext: { e in
relays.forEach { $0.accept(e) }
})
}
/**
Creates new subscription and sends elements to `ReplayRelay`.
This method can be only called from `MainThread`.
- parameter relays: Target relays for sequence elements.
- returns: Disposable object that can be used to unsubscribe the observer from the relay.
*/
public func drive(_ relays: ReplayRelay<Element>...) -> Disposable {
MainScheduler.ensureRunningOnMainThread(errorMessage: errorMessage)
return self.drive(onNext: { e in
relays.forEach { $0.accept(e) }
})
}
/**
Creates new subscription and sends elements to `ReplayRelay`.
This method can be only called from `MainThread`.
- parameter relays: Target relays for sequence elements.
- returns: Disposable object that can be used to unsubscribe the observer from the relay.
*/
public func drive(_ relays: ReplayRelay<Element?>...) -> Disposable {
MainScheduler.ensureRunningOnMainThread(errorMessage: errorMessage)
return self.drive(onNext: { e in
relays.forEach { $0.accept(e) }
})
}
/**
Subscribes to observable sequence using custom binder function.
This method can be only called from `MainThread`.
- parameter transformation: Function used to bind elements from `self`.
- returns: Object representing subscription.
*/
public func drive<Result>(_ transformation: (Observable<Element>) -> Result) -> Result {
MainScheduler.ensureRunningOnMainThread(errorMessage: errorMessage)
return transformation(self.asObservable())
}
/**
Subscribes to observable sequence using custom binder function and final parameter passed to binder function
after `self` is passed.
public func drive<R1, R2>(with: Self -> R1 -> R2, curriedArgument: R1) -> R2 {
return with(self)(curriedArgument)
}
This method can be only called from `MainThread`.
- parameter with: Function used to bind elements from `self`.
- parameter curriedArgument: Final argument passed to `binder` to finish binding process.
- returns: Object representing subscription.
*/
public func drive<R1, R2>(_ with: (Observable<Element>) -> (R1) -> R2, curriedArgument: R1) -> R2 {
MainScheduler.ensureRunningOnMainThread(errorMessage: errorMessage)
return with(self.asObservable())(curriedArgument)
}
/**
Subscribes an element handler, a completion handler and disposed handler to an observable sequence.
This method can be only called from `MainThread`.
Also, take in an object and provide an unretained, safe to use (i.e. not implicitly unwrapped), reference to it along with the events emitted by the sequence.
Error callback is not exposed because `Driver` can't error out.
- Note: If `object` can't be retained, none of the other closures will be invoked.
- parameter object: The object to provide an unretained reference on.
- parameter onNext: Action to invoke for each element in the observable sequence.
- parameter onCompleted: Action to invoke upon graceful termination of the observable sequence.
gracefully completed, errored, or if the generation is canceled by disposing subscription)
- parameter onDisposed: Action to invoke upon any type of termination of sequence (if the sequence has
gracefully completed, errored, or if the generation is canceled by disposing subscription)
- returns: Subscription object used to unsubscribe from the observable sequence.
*/
public func drive<Object: AnyObject>(
with object: Object,
onNext: ((Object, Element) -> Void)? = nil,
onCompleted: ((Object) -> Void)? = nil,
onDisposed: ((Object) -> Void)? = nil
) -> Disposable {
MainScheduler.ensureRunningOnMainThread(errorMessage: errorMessage)
return self.asObservable().subscribe(with: object, onNext: onNext, onCompleted: onCompleted, onDisposed: onDisposed)
}
/**
Subscribes an element handler, a completion handler and disposed handler to an observable sequence.
This method can be only called from `MainThread`.
Error callback is not exposed because `Driver` can't error out.
- parameter onNext: Action to invoke for each element in the observable sequence.
- parameter onCompleted: Action to invoke upon graceful termination of the observable sequence.
gracefully completed, errored, or if the generation is canceled by disposing subscription)
- parameter onDisposed: Action to invoke upon any type of termination of sequence (if the sequence has
gracefully completed, errored, or if the generation is canceled by disposing subscription)
- returns: Subscription object used to unsubscribe from the observable sequence.
*/
public func drive(
onNext: ((Element) -> Void)? = nil,
onCompleted: (() -> Void)? = nil,
onDisposed: (() -> Void)? = nil
) -> Disposable {
MainScheduler.ensureRunningOnMainThread(errorMessage: errorMessage)
return self.asObservable().subscribe(onNext: onNext, onCompleted: onCompleted, onDisposed: onDisposed)
}
/**
Subscribes to this `Driver` with a no-op.
This method can be only called from `MainThread`.
- note: This is an alias of `drive(onNext: nil, onCompleted: nil, onDisposed: nil)` used to fix an ambiguity bug in Swift: https://bugs.swift.org/browse/SR-13657
- returns: Subscription object used to unsubscribe from the observable sequence.
*/
public func drive() -> Disposable {
drive(onNext: nil, onCompleted: nil, onDisposed: nil)
}
}

View File

@@ -0,0 +1,53 @@
//
// Driver.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 9/26/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import RxSwift
/**
Trait that represents observable sequence with following properties:
- it never fails
- it delivers events on `MainScheduler.instance`
- `share(replay: 1, scope: .whileConnected)` sharing strategy
Additional explanation:
- all observers share sequence computation resources
- it's stateful, upon subscription (calling subscribe) last element is immediately replayed if it was produced
- computation of elements is reference counted with respect to the number of observers
- if there are no subscribers, it will release sequence computation resources
In case trait that models event bus is required, please check `Signal`.
`Driver<Element>` can be considered a builder pattern for observable sequences that drive the application.
If observable sequence has produced at least one element, after new subscription is made last produced element will be
immediately replayed on the same thread on which the subscription was made.
When using `drive*`, `subscribe*` and `bind*` family of methods, they should always be called from main thread.
If `drive*`, `subscribe*` and `bind*` are called from background thread, it is possible that initial replay
will happen on background thread, and subsequent events will arrive on main thread.
To find out more about traits and how to use them, please visit `Documentation/Traits.md`.
*/
public typealias Driver<Element> = SharedSequence<DriverSharingStrategy, Element>
public struct DriverSharingStrategy: SharingStrategyProtocol {
public static var scheduler: SchedulerType { SharingScheduler.make() }
public static func share<Element>(_ source: Observable<Element>) -> Observable<Element> {
source.share(replay: 1, scope: .whileConnected)
}
}
extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingStrategy {
/// Adds `asDriver` to `SharingSequence` with `DriverSharingStrategy`.
public func asDriver() -> Driver<Element> {
self.asSharedSequence()
}
}

View File

@@ -0,0 +1,57 @@
//
// ObservableConvertibleType+Driver.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 9/19/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import RxSwift
extension ObservableConvertibleType {
/**
Converts observable sequence to `Driver` trait.
- parameter onErrorJustReturn: Element to return in case of error and after that complete the sequence.
- returns: Driver trait.
*/
public func asDriver(onErrorJustReturn: Element) -> Driver<Element> {
let source = self
.asObservable()
.observe(on:DriverSharingStrategy.scheduler)
.catchAndReturn(onErrorJustReturn)
return Driver(source)
}
/**
Converts observable sequence to `Driver` trait.
- parameter onErrorDriveWith: Driver that continues to drive the sequence in case of error.
- returns: Driver trait.
*/
public func asDriver(onErrorDriveWith: Driver<Element>) -> Driver<Element> {
let source = self
.asObservable()
.observe(on:DriverSharingStrategy.scheduler)
.catch { _ in
onErrorDriveWith.asObservable()
}
return Driver(source)
}
/**
Converts observable sequence to `Driver` trait.
- parameter onErrorRecover: Calculates driver that continues to drive the sequence in case of error.
- returns: Driver trait.
*/
public func asDriver(onErrorRecover: @escaping (_ error: Swift.Error) -> Driver<Element>) -> Driver<Element> {
let source = self
.asObservable()
.observe(on:DriverSharingStrategy.scheduler)
.catch { error in
onErrorRecover(error).asObservable()
}
return Driver(source)
}
}

View File

@@ -0,0 +1,57 @@
//
// ObservableConvertibleType+SharedSequence.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 11/1/17.
// Copyright © 2017 Krunoslav Zaher. All rights reserved.
//
import RxSwift
extension ObservableConvertibleType {
/**
Converts anything convertible to `Observable` to `SharedSequence` unit.
- parameter onErrorJustReturn: Element to return in case of error and after that complete the sequence.
- returns: Driving observable sequence.
*/
public func asSharedSequence<S>(sharingStrategy: S.Type = S.self, onErrorJustReturn: Element) -> SharedSequence<S, Element> {
let source = self
.asObservable()
.observe(on:S.scheduler)
.catchAndReturn(onErrorJustReturn)
return SharedSequence(source)
}
/**
Converts anything convertible to `Observable` to `SharedSequence` unit.
- parameter onErrorDriveWith: SharedSequence that provides elements of the sequence in case of error.
- returns: Driving observable sequence.
*/
public func asSharedSequence<S>(sharingStrategy: S.Type = S.self, onErrorDriveWith: SharedSequence<S, Element>) -> SharedSequence<S, Element> {
let source = self
.asObservable()
.observe(on:S.scheduler)
.catch { _ in
onErrorDriveWith.asObservable()
}
return SharedSequence(source)
}
/**
Converts anything convertible to `Observable` to `SharedSequence` unit.
- parameter onErrorRecover: Calculates driver that continues to drive the sequence in case of error.
- returns: Driving observable sequence.
*/
public func asSharedSequence<S>(sharingStrategy: S.Type = S.self, onErrorRecover: @escaping (_ error: Swift.Error) -> SharedSequence<S, Element>) -> SharedSequence<S, Element> {
let source = self
.asObservable()
.observe(on:S.scheduler)
.catch { error in
onErrorRecover(error).asObservable()
}
return SharedSequence(source)
}
}

View File

@@ -0,0 +1,62 @@
//
// SchedulerType+SharedSequence.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 8/27/17.
// Copyright © 2017 Krunoslav Zaher. All rights reserved.
//
import RxSwift
public enum SharingScheduler {
/// Default scheduler used in SharedSequence based traits.
public private(set) static var make: () -> SchedulerType = { MainScheduler() }
/**
This method can be used in unit tests to ensure that built in shared sequences are using mock schedulers instead
of main schedulers.
**This shouldn't be used in normal release builds.**
*/
static public func mock(scheduler: SchedulerType, action: () throws -> Void) rethrows {
return try mock(makeScheduler: { scheduler }, action: action)
}
/**
This method can be used in unit tests to ensure that built in shared sequences are using mock schedulers instead
of main schedulers.
**This shouldn't be used in normal release builds.**
*/
static public func mock(makeScheduler: @escaping () -> SchedulerType, action: () throws -> Void) rethrows {
let originalMake = make
make = makeScheduler
defer {
make = originalMake
}
try action()
// If you remove this line , compiler buggy optimizations will change behavior of this code
_forceCompilerToStopDoingInsaneOptimizationsThatBreakCode(makeScheduler)
// Scary, I know
}
}
#if os(Linux)
import Glibc
#else
import Foundation
#endif
func _forceCompilerToStopDoingInsaneOptimizationsThatBreakCode(_ scheduler: () -> SchedulerType) {
let a: Int32 = 1
#if os(Linux)
let b = 314 + Int32(Glibc.random() & 1)
#else
let b = 314 + Int32(arc4random() & 1)
#endif
if a == b {
print(scheduler())
}
}

View File

@@ -0,0 +1,42 @@
//
// SharedSequence+Concurrency.swift
// RxCocoa
//
// Created by Shai Mishali on 22/09/2021.
// Copyright © 2021 Krunoslav Zaher. All rights reserved.
//
#if swift(>=5.5.2) && canImport(_Concurrency) && !os(Linux)
import Foundation
// MARK: - Shared Sequence
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
public extension SharedSequence {
/// Allows iterating over the values of this Shared Sequence
/// asynchronously via Swift's concurrency features (`async/await`)
///
/// A sample usage would look like so:
///
/// ```swift
/// for await value in driver.values {
/// // Handle emitted values
/// }
/// ```
@MainActor var values: AsyncStream<Element> {
AsyncStream { continuation in
// It is safe to ignore the `onError` closure here since
// Shared Sequences (`Driver` and `Signal`) cannot fail
let disposable = self.asObservable()
.subscribe(
onNext: { value in continuation.yield(value) },
onCompleted: { continuation.finish() },
onDisposed: { continuation.onTermination?(.cancelled) }
)
continuation.onTermination = { @Sendable _ in
disposable.dispose()
}
}
}
}
#endif

View File

@@ -0,0 +1,656 @@
// This file is autogenerated. Take a look at `Preprocessor` target in RxSwift project
//
// SharedSequence+Operators+arity.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 10/14/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import RxSwift
// 2
extension SharedSequence {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
- parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func zip<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, resultSelector: @escaping (O1.Element, O2.Element) throws -> Element)
-> SharedSequence<O1.SharingStrategy, Element> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy {
let source = Observable.zip(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(),
resultSelector: resultSelector
)
return SharedSequence<SharingStrategy, Element>(source)
}
}
extension SharedSequenceConvertibleType where Element == Any {
/**
Merges the specified observable sequences into one observable sequence of element tuples whenever all of the observable sequences have produced an element at a corresponding index.
- returns: An observable sequence containing the result of combining elements of the sources.
*/
public static func zip<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2)
-> SharedSequence<O1.SharingStrategy, (O1.Element, O2.Element)> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy {
let source = Observable.zip(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable()
)
return SharedSequence<SharingStrategy, (O1.Element, O2.Element)>(source)
}
}
extension SharedSequence {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
- parameter resultSelector: Function to invoke whenever any of the sources produces an element.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func combineLatest<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, resultSelector: @escaping (O1.Element, O2.Element) throws -> Element)
-> SharedSequence<SharingStrategy, Element> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy {
let source = Observable.combineLatest(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(),
resultSelector: resultSelector
)
return SharedSequence<O1.SharingStrategy, Element>(source)
}
}
extension SharedSequenceConvertibleType where Element == Any {
/**
Merges the specified observable sequences into one observable sequence of element tuples whenever any of the observable sequences produces an element.
- returns: An observable sequence containing the result of combining elements of the sources.
*/
public static func combineLatest<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2)
-> SharedSequence<SharingStrategy, (O1.Element, O2.Element)> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy {
let source = Observable.combineLatest(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable()
)
return SharedSequence<O1.SharingStrategy, (O1.Element, O2.Element)>(source)
}
}
// 3
extension SharedSequence {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
- parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func zip<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, resultSelector: @escaping (O1.Element, O2.Element, O3.Element) throws -> Element)
-> SharedSequence<O1.SharingStrategy, Element> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy {
let source = Observable.zip(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(),
resultSelector: resultSelector
)
return SharedSequence<SharingStrategy, Element>(source)
}
}
extension SharedSequenceConvertibleType where Element == Any {
/**
Merges the specified observable sequences into one observable sequence of element tuples whenever all of the observable sequences have produced an element at a corresponding index.
- returns: An observable sequence containing the result of combining elements of the sources.
*/
public static func zip<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3)
-> SharedSequence<O1.SharingStrategy, (O1.Element, O2.Element, O3.Element)> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy {
let source = Observable.zip(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable()
)
return SharedSequence<SharingStrategy, (O1.Element, O2.Element, O3.Element)>(source)
}
}
extension SharedSequence {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
- parameter resultSelector: Function to invoke whenever any of the sources produces an element.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func combineLatest<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, resultSelector: @escaping (O1.Element, O2.Element, O3.Element) throws -> Element)
-> SharedSequence<SharingStrategy, Element> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy {
let source = Observable.combineLatest(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(),
resultSelector: resultSelector
)
return SharedSequence<O1.SharingStrategy, Element>(source)
}
}
extension SharedSequenceConvertibleType where Element == Any {
/**
Merges the specified observable sequences into one observable sequence of element tuples whenever any of the observable sequences produces an element.
- returns: An observable sequence containing the result of combining elements of the sources.
*/
public static func combineLatest<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3)
-> SharedSequence<SharingStrategy, (O1.Element, O2.Element, O3.Element)> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy {
let source = Observable.combineLatest(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable()
)
return SharedSequence<O1.SharingStrategy, (O1.Element, O2.Element, O3.Element)>(source)
}
}
// 4
extension SharedSequence {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
- parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func zip<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element) throws -> Element)
-> SharedSequence<O1.SharingStrategy, Element> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy {
let source = Observable.zip(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(),
resultSelector: resultSelector
)
return SharedSequence<SharingStrategy, Element>(source)
}
}
extension SharedSequenceConvertibleType where Element == Any {
/**
Merges the specified observable sequences into one observable sequence of element tuples whenever all of the observable sequences have produced an element at a corresponding index.
- returns: An observable sequence containing the result of combining elements of the sources.
*/
public static func zip<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4)
-> SharedSequence<O1.SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element)> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy {
let source = Observable.zip(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable()
)
return SharedSequence<SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element)>(source)
}
}
extension SharedSequence {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
- parameter resultSelector: Function to invoke whenever any of the sources produces an element.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func combineLatest<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element) throws -> Element)
-> SharedSequence<SharingStrategy, Element> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy {
let source = Observable.combineLatest(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(),
resultSelector: resultSelector
)
return SharedSequence<O1.SharingStrategy, Element>(source)
}
}
extension SharedSequenceConvertibleType where Element == Any {
/**
Merges the specified observable sequences into one observable sequence of element tuples whenever any of the observable sequences produces an element.
- returns: An observable sequence containing the result of combining elements of the sources.
*/
public static func combineLatest<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4)
-> SharedSequence<SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element)> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy {
let source = Observable.combineLatest(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable()
)
return SharedSequence<O1.SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element)>(source)
}
}
// 5
extension SharedSequence {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
- parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func zip<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType, O5: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element) throws -> Element)
-> SharedSequence<O1.SharingStrategy, Element> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy,
SharingStrategy == O5.SharingStrategy {
let source = Observable.zip(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(),
resultSelector: resultSelector
)
return SharedSequence<SharingStrategy, Element>(source)
}
}
extension SharedSequenceConvertibleType where Element == Any {
/**
Merges the specified observable sequences into one observable sequence of element tuples whenever all of the observable sequences have produced an element at a corresponding index.
- returns: An observable sequence containing the result of combining elements of the sources.
*/
public static func zip<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType, O5: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5)
-> SharedSequence<O1.SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element)> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy,
SharingStrategy == O5.SharingStrategy {
let source = Observable.zip(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable()
)
return SharedSequence<SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element)>(source)
}
}
extension SharedSequence {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
- parameter resultSelector: Function to invoke whenever any of the sources produces an element.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func combineLatest<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType, O5: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element) throws -> Element)
-> SharedSequence<SharingStrategy, Element> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy,
SharingStrategy == O5.SharingStrategy {
let source = Observable.combineLatest(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(),
resultSelector: resultSelector
)
return SharedSequence<O1.SharingStrategy, Element>(source)
}
}
extension SharedSequenceConvertibleType where Element == Any {
/**
Merges the specified observable sequences into one observable sequence of element tuples whenever any of the observable sequences produces an element.
- returns: An observable sequence containing the result of combining elements of the sources.
*/
public static func combineLatest<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType, O5: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5)
-> SharedSequence<SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element)> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy,
SharingStrategy == O5.SharingStrategy {
let source = Observable.combineLatest(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable()
)
return SharedSequence<O1.SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element)>(source)
}
}
// 6
extension SharedSequence {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
- parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func zip<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType, O5: SharedSequenceConvertibleType, O6: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element) throws -> Element)
-> SharedSequence<O1.SharingStrategy, Element> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy,
SharingStrategy == O5.SharingStrategy,
SharingStrategy == O6.SharingStrategy {
let source = Observable.zip(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(),
resultSelector: resultSelector
)
return SharedSequence<SharingStrategy, Element>(source)
}
}
extension SharedSequenceConvertibleType where Element == Any {
/**
Merges the specified observable sequences into one observable sequence of element tuples whenever all of the observable sequences have produced an element at a corresponding index.
- returns: An observable sequence containing the result of combining elements of the sources.
*/
public static func zip<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType, O5: SharedSequenceConvertibleType, O6: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6)
-> SharedSequence<O1.SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element)> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy,
SharingStrategy == O5.SharingStrategy,
SharingStrategy == O6.SharingStrategy {
let source = Observable.zip(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable()
)
return SharedSequence<SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element)>(source)
}
}
extension SharedSequence {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
- parameter resultSelector: Function to invoke whenever any of the sources produces an element.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func combineLatest<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType, O5: SharedSequenceConvertibleType, O6: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element) throws -> Element)
-> SharedSequence<SharingStrategy, Element> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy,
SharingStrategy == O5.SharingStrategy,
SharingStrategy == O6.SharingStrategy {
let source = Observable.combineLatest(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(),
resultSelector: resultSelector
)
return SharedSequence<O1.SharingStrategy, Element>(source)
}
}
extension SharedSequenceConvertibleType where Element == Any {
/**
Merges the specified observable sequences into one observable sequence of element tuples whenever any of the observable sequences produces an element.
- returns: An observable sequence containing the result of combining elements of the sources.
*/
public static func combineLatest<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType, O5: SharedSequenceConvertibleType, O6: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6)
-> SharedSequence<SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element)> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy,
SharingStrategy == O5.SharingStrategy,
SharingStrategy == O6.SharingStrategy {
let source = Observable.combineLatest(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable()
)
return SharedSequence<O1.SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element)>(source)
}
}
// 7
extension SharedSequence {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
- parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func zip<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType, O5: SharedSequenceConvertibleType, O6: SharedSequenceConvertibleType, O7: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element) throws -> Element)
-> SharedSequence<O1.SharingStrategy, Element> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy,
SharingStrategy == O5.SharingStrategy,
SharingStrategy == O6.SharingStrategy,
SharingStrategy == O7.SharingStrategy {
let source = Observable.zip(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), source7.asSharedSequence().asObservable(),
resultSelector: resultSelector
)
return SharedSequence<SharingStrategy, Element>(source)
}
}
extension SharedSequenceConvertibleType where Element == Any {
/**
Merges the specified observable sequences into one observable sequence of element tuples whenever all of the observable sequences have produced an element at a corresponding index.
- returns: An observable sequence containing the result of combining elements of the sources.
*/
public static func zip<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType, O5: SharedSequenceConvertibleType, O6: SharedSequenceConvertibleType, O7: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7)
-> SharedSequence<O1.SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element)> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy,
SharingStrategy == O5.SharingStrategy,
SharingStrategy == O6.SharingStrategy,
SharingStrategy == O7.SharingStrategy {
let source = Observable.zip(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), source7.asSharedSequence().asObservable()
)
return SharedSequence<SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element)>(source)
}
}
extension SharedSequence {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
- parameter resultSelector: Function to invoke whenever any of the sources produces an element.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func combineLatest<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType, O5: SharedSequenceConvertibleType, O6: SharedSequenceConvertibleType, O7: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element) throws -> Element)
-> SharedSequence<SharingStrategy, Element> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy,
SharingStrategy == O5.SharingStrategy,
SharingStrategy == O6.SharingStrategy,
SharingStrategy == O7.SharingStrategy {
let source = Observable.combineLatest(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), source7.asSharedSequence().asObservable(),
resultSelector: resultSelector
)
return SharedSequence<O1.SharingStrategy, Element>(source)
}
}
extension SharedSequenceConvertibleType where Element == Any {
/**
Merges the specified observable sequences into one observable sequence of element tuples whenever any of the observable sequences produces an element.
- returns: An observable sequence containing the result of combining elements of the sources.
*/
public static func combineLatest<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType, O5: SharedSequenceConvertibleType, O6: SharedSequenceConvertibleType, O7: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7)
-> SharedSequence<SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element)> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy,
SharingStrategy == O5.SharingStrategy,
SharingStrategy == O6.SharingStrategy,
SharingStrategy == O7.SharingStrategy {
let source = Observable.combineLatest(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), source7.asSharedSequence().asObservable()
)
return SharedSequence<O1.SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element)>(source)
}
}
// 8
extension SharedSequence {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
- parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func zip<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType, O5: SharedSequenceConvertibleType, O6: SharedSequenceConvertibleType, O7: SharedSequenceConvertibleType, O8: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element) throws -> Element)
-> SharedSequence<O1.SharingStrategy, Element> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy,
SharingStrategy == O5.SharingStrategy,
SharingStrategy == O6.SharingStrategy,
SharingStrategy == O7.SharingStrategy,
SharingStrategy == O8.SharingStrategy {
let source = Observable.zip(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), source7.asSharedSequence().asObservable(), source8.asSharedSequence().asObservable(),
resultSelector: resultSelector
)
return SharedSequence<SharingStrategy, Element>(source)
}
}
extension SharedSequenceConvertibleType where Element == Any {
/**
Merges the specified observable sequences into one observable sequence of element tuples whenever all of the observable sequences have produced an element at a corresponding index.
- returns: An observable sequence containing the result of combining elements of the sources.
*/
public static func zip<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType, O5: SharedSequenceConvertibleType, O6: SharedSequenceConvertibleType, O7: SharedSequenceConvertibleType, O8: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8)
-> SharedSequence<O1.SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element)> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy,
SharingStrategy == O5.SharingStrategy,
SharingStrategy == O6.SharingStrategy,
SharingStrategy == O7.SharingStrategy,
SharingStrategy == O8.SharingStrategy {
let source = Observable.zip(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), source7.asSharedSequence().asObservable(), source8.asSharedSequence().asObservable()
)
return SharedSequence<SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element)>(source)
}
}
extension SharedSequence {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
- parameter resultSelector: Function to invoke whenever any of the sources produces an element.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func combineLatest<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType, O5: SharedSequenceConvertibleType, O6: SharedSequenceConvertibleType, O7: SharedSequenceConvertibleType, O8: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element) throws -> Element)
-> SharedSequence<SharingStrategy, Element> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy,
SharingStrategy == O5.SharingStrategy,
SharingStrategy == O6.SharingStrategy,
SharingStrategy == O7.SharingStrategy,
SharingStrategy == O8.SharingStrategy {
let source = Observable.combineLatest(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), source7.asSharedSequence().asObservable(), source8.asSharedSequence().asObservable(),
resultSelector: resultSelector
)
return SharedSequence<O1.SharingStrategy, Element>(source)
}
}
extension SharedSequenceConvertibleType where Element == Any {
/**
Merges the specified observable sequences into one observable sequence of element tuples whenever any of the observable sequences produces an element.
- returns: An observable sequence containing the result of combining elements of the sources.
*/
public static func combineLatest<O1: SharedSequenceConvertibleType, O2: SharedSequenceConvertibleType, O3: SharedSequenceConvertibleType, O4: SharedSequenceConvertibleType, O5: SharedSequenceConvertibleType, O6: SharedSequenceConvertibleType, O7: SharedSequenceConvertibleType, O8: SharedSequenceConvertibleType>
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8)
-> SharedSequence<SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element)> where SharingStrategy == O1.SharingStrategy,
SharingStrategy == O2.SharingStrategy,
SharingStrategy == O3.SharingStrategy,
SharingStrategy == O4.SharingStrategy,
SharingStrategy == O5.SharingStrategy,
SharingStrategy == O6.SharingStrategy,
SharingStrategy == O7.SharingStrategy,
SharingStrategy == O8.SharingStrategy {
let source = Observable.combineLatest(
source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), source7.asSharedSequence().asObservable(), source8.asSharedSequence().asObservable()
)
return SharedSequence<O1.SharingStrategy, (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element)>(source)
}
}

View File

@@ -0,0 +1,584 @@
//
// SharedSequence+Operators.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 9/19/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import RxSwift
// MARK: map
extension SharedSequenceConvertibleType {
/**
Projects each element of an observable sequence into a new form.
- parameter selector: A transform function to apply to each source element.
- returns: An observable sequence whose elements are the result of invoking the transform function on each element of source.
*/
public func map<Result>(_ selector: @escaping (Element) -> Result) -> SharedSequence<SharingStrategy, Result> {
let source = self
.asObservable()
.map(selector)
return SharedSequence<SharingStrategy, Result>(source)
}
}
// MARK: compactMap
extension SharedSequenceConvertibleType {
/**
Projects each element of an observable sequence into an optional form and filters all optional results.
- parameter selector: A transform function to apply to each source element and which returns an element or nil.
- returns: An observable sequence whose elements are the result of filtering the transform function for each element of the source.
*/
public func compactMap<Result>(_ selector: @escaping (Element) -> Result?) -> SharedSequence<SharingStrategy, Result> {
let source = self
.asObservable()
.compactMap(selector)
return SharedSequence<SharingStrategy, Result>(source)
}
}
// MARK: filter
extension SharedSequenceConvertibleType {
/**
Filters the elements of an observable sequence based on a predicate.
- parameter predicate: A function to test each source element for a condition.
- returns: An observable sequence that contains elements from the input sequence that satisfy the condition.
*/
public func filter(_ predicate: @escaping (Element) -> Bool) -> SharedSequence<SharingStrategy, Element> {
let source = self
.asObservable()
.filter(predicate)
return SharedSequence(source)
}
}
// MARK: switchLatest
extension SharedSequenceConvertibleType where Element: SharedSequenceConvertibleType {
/**
Transforms an observable sequence of observable sequences into an observable sequence
producing values only from the most recent observable sequence.
Each time a new inner observable sequence is received, unsubscribe from the
previous inner observable sequence.
- returns: The observable sequence that at any point in time produces the elements of the most recent inner observable sequence that has been received.
*/
public func switchLatest() -> SharedSequence<Element.SharingStrategy, Element.Element> {
let source: Observable<Element.Element> = self
.asObservable()
.map { $0.asSharedSequence() }
.switchLatest()
return SharedSequence<Element.SharingStrategy, Element.Element>(source)
}
}
// MARK: flatMapLatest
extension SharedSequenceConvertibleType {
/**
Projects each element of an observable sequence into a new sequence of observable sequences and then
transforms an observable sequence of observable sequences into an observable sequence producing values only from the most recent observable sequence.
It is a combination of `map` + `switchLatest` operator
- parameter selector: A transform function to apply to each element.
- returns: An observable sequence whose elements are the result of invoking the transform function on each element of source producing an
Observable of Observable sequences and that at any point in time produces the elements of the most recent inner observable sequence that has been received.
*/
public func flatMapLatest<Sharing, Result>(_ selector: @escaping (Element) -> SharedSequence<Sharing, Result>)
-> SharedSequence<Sharing, Result> {
let source: Observable<Result> = self
.asObservable()
.flatMapLatest(selector)
return SharedSequence<Sharing, Result>(source)
}
}
// MARK: flatMapFirst
extension SharedSequenceConvertibleType {
/**
Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence.
If element is received while there is some projected observable sequence being merged it will simply be ignored.
- parameter selector: A transform function to apply to element that was observed while no observable is executing in parallel.
- returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence that was received while no other sequence was being calculated.
*/
public func flatMapFirst<Sharing, Result>(_ selector: @escaping (Element) -> SharedSequence<Sharing, Result>)
-> SharedSequence<Sharing, Result> {
let source: Observable<Result> = self
.asObservable()
.flatMapFirst(selector)
return SharedSequence<Sharing, Result>(source)
}
}
// MARK: do
extension SharedSequenceConvertibleType {
/**
Invokes an action for each event in the observable sequence, and propagates all observer messages through the result sequence.
- parameter onNext: Action to invoke for each element in the observable sequence.
- parameter afterNext: Action to invoke for each element after the observable has passed an onNext event along to its downstream.
- parameter onCompleted: Action to invoke upon graceful termination of the observable sequence.
- parameter afterCompleted: Action to invoke after graceful termination of the observable sequence.
- parameter onSubscribe: Action to invoke before subscribing to source observable sequence.
- parameter onSubscribed: Action to invoke after subscribing to source observable sequence.
- parameter onDispose: Action to invoke after subscription to source observable has been disposed for any reason. It can be either because sequence terminates for some reason or observer subscription being disposed.
- returns: The source sequence with the side-effecting behavior applied.
*/
public func `do`(onNext: ((Element) -> Void)? = nil, afterNext: ((Element) -> Void)? = nil, onCompleted: (() -> Void)? = nil, afterCompleted: (() -> Void)? = nil, onSubscribe: (() -> Void)? = nil, onSubscribed: (() -> Void)? = nil, onDispose: (() -> Void)? = nil)
-> SharedSequence<SharingStrategy, Element> {
let source = self.asObservable()
.do(onNext: onNext, afterNext: afterNext, onCompleted: onCompleted, afterCompleted: afterCompleted, onSubscribe: onSubscribe, onSubscribed: onSubscribed, onDispose: onDispose)
return SharedSequence(source)
}
}
// MARK: debug
extension SharedSequenceConvertibleType {
/**
Prints received events for all observers on standard output.
- parameter identifier: Identifier that is printed together with event description to standard output.
- returns: An observable sequence whose events are printed to standard output.
*/
public func debug(_ identifier: String? = nil, trimOutput: Bool = false, file: String = #file, line: UInt = #line, function: String = #function) -> SharedSequence<SharingStrategy, Element> {
let source = self.asObservable()
.debug(identifier, trimOutput: trimOutput, file: file, line: line, function: function)
return SharedSequence(source)
}
}
// MARK: distinctUntilChanged
extension SharedSequenceConvertibleType where Element: Equatable {
/**
Returns an observable sequence that contains only distinct contiguous elements according to equality operator.
- returns: An observable sequence only containing the distinct contiguous elements, based on equality operator, from the source sequence.
*/
public func distinctUntilChanged()
-> SharedSequence<SharingStrategy, Element> {
let source = self.asObservable()
.distinctUntilChanged({ $0 }, comparer: { ($0 == $1) })
return SharedSequence(source)
}
}
extension SharedSequenceConvertibleType {
/**
Returns an observable sequence that contains only distinct contiguous elements according to the `keySelector`.
- parameter keySelector: A function to compute the comparison key for each element.
- returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value, from the source sequence.
*/
public func distinctUntilChanged<Key: Equatable>(_ keySelector: @escaping (Element) -> Key) -> SharedSequence<SharingStrategy, Element> {
let source = self.asObservable()
.distinctUntilChanged(keySelector, comparer: { $0 == $1 })
return SharedSequence(source)
}
/**
Returns an observable sequence that contains only distinct contiguous elements according to the `comparer`.
- parameter comparer: Equality comparer for computed key values.
- returns: An observable sequence only containing the distinct contiguous elements, based on `comparer`, from the source sequence.
*/
public func distinctUntilChanged(_ comparer: @escaping (Element, Element) -> Bool) -> SharedSequence<SharingStrategy, Element> {
let source = self.asObservable()
.distinctUntilChanged({ $0 }, comparer: comparer)
return SharedSequence<SharingStrategy, Element>(source)
}
/**
Returns an observable sequence that contains only distinct contiguous elements according to the keySelector and the comparer.
- parameter keySelector: A function to compute the comparison key for each element.
- parameter comparer: Equality comparer for computed key values.
- returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value and the comparer, from the source sequence.
*/
public func distinctUntilChanged<K>(_ keySelector: @escaping (Element) -> K, comparer: @escaping (K, K) -> Bool) -> SharedSequence<SharingStrategy, Element> {
let source = self.asObservable()
.distinctUntilChanged(keySelector, comparer: comparer)
return SharedSequence<SharingStrategy, Element>(source)
}
}
// MARK: flatMap
extension SharedSequenceConvertibleType {
/**
Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence.
- parameter selector: A transform function to apply to each element.
- returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence.
*/
public func flatMap<Sharing, Result>(_ selector: @escaping (Element) -> SharedSequence<Sharing, Result>) -> SharedSequence<Sharing, Result> {
let source = self.asObservable()
.flatMap(selector)
return SharedSequence(source)
}
}
// MARK: merge
extension SharedSequenceConvertibleType {
/**
Merges elements from all observable sequences from collection into a single observable sequence.
- seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html)
- parameter sources: Collection of observable sequences to merge.
- returns: The observable sequence that merges the elements of the observable sequences.
*/
public static func merge<Collection: Swift.Collection>(_ sources: Collection) -> SharedSequence<SharingStrategy, Element>
where Collection.Element == SharedSequence<SharingStrategy, Element> {
let source = Observable.merge(sources.map { $0.asObservable() })
return SharedSequence<SharingStrategy, Element>(source)
}
/**
Merges elements from all observable sequences from array into a single observable sequence.
- seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html)
- parameter sources: Array of observable sequences to merge.
- returns: The observable sequence that merges the elements of the observable sequences.
*/
public static func merge(_ sources: [SharedSequence<SharingStrategy, Element>]) -> SharedSequence<SharingStrategy, Element> {
let source = Observable.merge(sources.map { $0.asObservable() })
return SharedSequence<SharingStrategy, Element>(source)
}
/**
Merges elements from all observable sequences into a single observable sequence.
- seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html)
- parameter sources: Collection of observable sequences to merge.
- returns: The observable sequence that merges the elements of the observable sequences.
*/
public static func merge(_ sources: SharedSequence<SharingStrategy, Element>...) -> SharedSequence<SharingStrategy, Element> {
let source = Observable.merge(sources.map { $0.asObservable() })
return SharedSequence<SharingStrategy, Element>(source)
}
}
// MARK: merge
extension SharedSequenceConvertibleType where Element: SharedSequenceConvertibleType {
/**
Merges elements from all observable sequences in the given enumerable sequence into a single observable sequence.
- returns: The observable sequence that merges the elements of the observable sequences.
*/
public func merge() -> SharedSequence<Element.SharingStrategy, Element.Element> {
let source = self.asObservable()
.map { $0.asSharedSequence() }
.merge()
return SharedSequence<Element.SharingStrategy, Element.Element>(source)
}
/**
Merges elements from all inner observable sequences into a single observable sequence, limiting the number of concurrent subscriptions to inner sequences.
- parameter maxConcurrent: Maximum number of inner observable sequences being subscribed to concurrently.
- returns: The observable sequence that merges the elements of the inner sequences.
*/
public func merge(maxConcurrent: Int)
-> SharedSequence<Element.SharingStrategy, Element.Element> {
let source = self.asObservable()
.map { $0.asSharedSequence() }
.merge(maxConcurrent: maxConcurrent)
return SharedSequence<Element.SharingStrategy, Element.Element>(source)
}
}
// MARK: throttle
extension SharedSequenceConvertibleType {
/**
Returns an Observable that emits the first and the latest item emitted by the source Observable during sequential time windows of a specified duration.
This operator makes sure that no two elements are emitted in less then dueTime.
- seealso: [debounce operator on reactivex.io](http://reactivex.io/documentation/operators/debounce.html)
- parameter dueTime: Throttling duration for each element.
- parameter latest: Should latest element received in a dueTime wide time window since last element emission be emitted.
- returns: The throttled sequence.
*/
public func throttle(_ dueTime: RxTimeInterval, latest: Bool = true)
-> SharedSequence<SharingStrategy, Element> {
let source = self.asObservable()
.throttle(dueTime, latest: latest, scheduler: SharingStrategy.scheduler)
return SharedSequence(source)
}
/**
Ignores elements from an observable sequence which are followed by another element within a specified relative time duration, using the specified scheduler to run throttling timers.
- parameter dueTime: Throttling duration for each element.
- returns: The throttled sequence.
*/
public func debounce(_ dueTime: RxTimeInterval)
-> SharedSequence<SharingStrategy, Element> {
let source = self.asObservable()
.debounce(dueTime, scheduler: SharingStrategy.scheduler)
return SharedSequence(source)
}
}
// MARK: scan
extension SharedSequenceConvertibleType {
/**
Applies an accumulator function over an observable sequence and returns each intermediate result. The specified seed value is used as the initial accumulator value.
For aggregation behavior with no intermediate results, see `reduce`.
- parameter seed: The initial accumulator value.
- parameter accumulator: An accumulator function to be invoked on each element.
- returns: An observable sequence containing the accumulated values.
*/
public func scan<A>(_ seed: A, accumulator: @escaping (A, Element) -> A)
-> SharedSequence<SharingStrategy, A> {
let source = self.asObservable()
.scan(seed, accumulator: accumulator)
return SharedSequence<SharingStrategy, A>(source)
}
}
// MARK: concat
extension SharedSequence {
/**
Concatenates all observable sequences in the given sequence, as long as the previous observable sequence terminated successfully.
- returns: An observable sequence that contains the elements of each given sequence, in sequential order.
*/
public static func concat<Sequence: Swift.Sequence>(_ sequence: Sequence) -> SharedSequence<SharingStrategy, Element>
where Sequence.Element == SharedSequence<SharingStrategy, Element> {
let source = Observable.concat(sequence.lazy.map { $0.asObservable() })
return SharedSequence<SharingStrategy, Element>(source)
}
/**
Concatenates all observable sequences in the given sequence, as long as the previous observable sequence terminated successfully.
- returns: An observable sequence that contains the elements of each given sequence, in sequential order.
*/
public static func concat<Collection: Swift.Collection>(_ collection: Collection) -> SharedSequence<SharingStrategy, Element>
where Collection.Element == SharedSequence<SharingStrategy, Element> {
let source = Observable.concat(collection.map { $0.asObservable() })
return SharedSequence<SharingStrategy, Element>(source)
}
}
// MARK: zip
extension SharedSequence {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
- parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func zip<Collection: Swift.Collection, Result>(_ collection: Collection, resultSelector: @escaping ([Element]) throws -> Result) -> SharedSequence<SharingStrategy, Result>
where Collection.Element == SharedSequence<SharingStrategy, Element> {
let source = Observable.zip(collection.map { $0.asSharedSequence().asObservable() }, resultSelector: resultSelector)
return SharedSequence<SharingStrategy, Result>(source)
}
/**
Merges the specified observable sequences into one observable sequence all of the observable sequences have produced an element at a corresponding index.
- returns: An observable sequence containing the result of combining elements of the sources.
*/
public static func zip<Collection: Swift.Collection>(_ collection: Collection) -> SharedSequence<SharingStrategy, [Element]>
where Collection.Element == SharedSequence<SharingStrategy, Element> {
let source = Observable.zip(collection.map { $0.asSharedSequence().asObservable() })
return SharedSequence<SharingStrategy, [Element]>(source)
}
}
// MARK: combineLatest
extension SharedSequence {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
- parameter resultSelector: Function to invoke whenever any of the sources produces an element.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func combineLatest<Collection: Swift.Collection, Result>(_ collection: Collection, resultSelector: @escaping ([Element]) throws -> Result) -> SharedSequence<SharingStrategy, Result>
where Collection.Element == SharedSequence<SharingStrategy, Element> {
let source = Observable.combineLatest(collection.map { $0.asObservable() }, resultSelector: resultSelector)
return SharedSequence<SharingStrategy, Result>(source)
}
/**
Merges the specified observable sequences into one observable sequence whenever any of the observable sequences produces an element.
- returns: An observable sequence containing the result of combining elements of the sources.
*/
public static func combineLatest<Collection: Swift.Collection>(_ collection: Collection) -> SharedSequence<SharingStrategy, [Element]>
where Collection.Element == SharedSequence<SharingStrategy, Element> {
let source = Observable.combineLatest(collection.map { $0.asObservable() })
return SharedSequence<SharingStrategy, [Element]>(source)
}
}
// MARK: - withUnretained
extension SharedSequenceConvertibleType where SharingStrategy == SignalSharingStrategy {
/**
Provides an unretained, safe to use (i.e. not implicitly unwrapped), reference to an object along with the events emitted by the sequence.
In the case the provided object cannot be retained successfully, the sequence will complete.
- note: Be careful when using this operator in a sequence that has a buffer or replay, for example `share(replay: 1)`, as the sharing buffer will also include the provided object, which could potentially cause a retain cycle.
- parameter obj: The object to provide an unretained reference on.
- parameter resultSelector: A function to combine the unretained referenced on `obj` and the value of the observable sequence.
- returns: An observable sequence that contains the result of `resultSelector` being called with an unretained reference on `obj` and the values of the original sequence.
*/
public func withUnretained<Object: AnyObject, Out>(
_ obj: Object,
resultSelector: @escaping (Object, Element) -> Out
) -> SharedSequence<SharingStrategy, Out> {
SharedSequence(self.asObservable().withUnretained(obj, resultSelector: resultSelector))
}
/**
Provides an unretained, safe to use (i.e. not implicitly unwrapped), reference to an object along with the events emitted by the sequence.
In the case the provided object cannot be retained successfully, the sequence will complete.
- note: Be careful when using this operator in a sequence that has a buffer or replay, for example `share(replay: 1)`, as the sharing buffer will also include the provided object, which could potentially cause a retain cycle.
- parameter obj: The object to provide an unretained reference on.
- returns: An observable sequence of tuples that contains both an unretained reference on `obj` and the values of the original sequence.
*/
public func withUnretained<Object: AnyObject>(_ obj: Object) -> SharedSequence<SharingStrategy, (Object, Element)> {
withUnretained(obj) { ($0, $1) }
}
}
extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingStrategy {
@available(*, message: "withUnretained has been deprecated for Driver. Consider using `drive(with:onNext:onCompleted:onDisposed:)`, instead", unavailable)
public func withUnretained<Object: AnyObject, Out>(
_ obj: Object,
resultSelector: @escaping (Object, Element) -> Out
) -> SharedSequence<SharingStrategy, Out> {
SharedSequence(self.asObservable().withUnretained(obj, resultSelector: resultSelector))
}
@available(*, message: "withUnretained has been deprecated for Driver. Consider using `drive(with:onNext:onCompleted:onDisposed:)`, instead", unavailable)
public func withUnretained<Object: AnyObject>(_ obj: Object) -> SharedSequence<SharingStrategy, (Object, Element)> {
SharedSequence(self.asObservable().withUnretained(obj) { ($0, $1) })
}
}
// MARK: withLatestFrom
extension SharedSequenceConvertibleType {
/**
Merges two observable sequences into one observable sequence by combining each element from self with the latest element from the second source, if any.
- parameter second: Second observable source.
- parameter resultSelector: Function to invoke for each element from the self combined with the latest element from the second source, if any.
- returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function.
*/
public func withLatestFrom<SecondO: SharedSequenceConvertibleType, ResultType>(_ second: SecondO, resultSelector: @escaping (Element, SecondO.Element) -> ResultType) -> SharedSequence<SharingStrategy, ResultType> where SecondO.SharingStrategy == SharingStrategy {
let source = self.asObservable()
.withLatestFrom(second.asSharedSequence(), resultSelector: resultSelector)
return SharedSequence<SharingStrategy, ResultType>(source)
}
/**
Merges two observable sequences into one observable sequence by using latest element from the second sequence every time when `self` emits an element.
- parameter second: Second observable source.
- returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function.
*/
public func withLatestFrom<SecondO: SharedSequenceConvertibleType>(_ second: SecondO) -> SharedSequence<SharingStrategy, SecondO.Element> {
let source = self.asObservable()
.withLatestFrom(second.asSharedSequence())
return SharedSequence<SharingStrategy, SecondO.Element>(source)
}
}
// MARK: skip
extension SharedSequenceConvertibleType {
/**
Bypasses a specified number of elements in an observable sequence and then returns the remaining elements.
- seealso: [skip operator on reactivex.io](http://reactivex.io/documentation/operators/skip.html)
- parameter count: The number of elements to skip before returning the remaining elements.
- returns: An observable sequence that contains the elements that occur after the specified index in the input sequence.
*/
public func skip(_ count: Int)
-> SharedSequence<SharingStrategy, Element> {
let source = self.asObservable()
.skip(count)
return SharedSequence(source)
}
}
// MARK: startWith
extension SharedSequenceConvertibleType {
/**
Prepends a value to an observable sequence.
- seealso: [startWith operator on reactivex.io](http://reactivex.io/documentation/operators/startwith.html)
- parameter element: Element to prepend to the specified sequence.
- returns: The source sequence prepended with the specified values.
*/
public func startWith(_ element: Element)
-> SharedSequence<SharingStrategy, Element> {
let source = self.asObservable()
.startWith(element)
return SharedSequence(source)
}
}
// MARK: delay
extension SharedSequenceConvertibleType {
/**
Returns an observable sequence by the source observable sequence shifted forward in time by a specified delay. Error events from the source observable sequence are not delayed.
- seealso: [delay operator on reactivex.io](http://reactivex.io/documentation/operators/delay.html)
- parameter dueTime: Relative time shift of the source by.
- returns: the source Observable shifted in time by the specified delay.
*/
public func delay(_ dueTime: RxTimeInterval)
-> SharedSequence<SharingStrategy, Element> {
let source = self.asObservable()
.delay(dueTime, scheduler: SharingStrategy.scheduler)
return SharedSequence(source)
}
}

View File

@@ -0,0 +1,226 @@
//
// SharedSequence.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 8/27/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import RxSwift
/**
Trait that represents observable sequence that shares computation resources with following properties:
- it never fails
- it delivers events on `SharingStrategy.scheduler`
- sharing strategy is customizable using `SharingStrategy.share` behavior
`SharedSequence<Element>` can be considered a builder pattern for observable sequences that share computation resources.
To find out more about units and how to use them, please visit `Documentation/Traits.md`.
*/
public struct SharedSequence<SharingStrategy: SharingStrategyProtocol, Element> : SharedSequenceConvertibleType, ObservableConvertibleType {
let source: Observable<Element>
init(_ source: Observable<Element>) {
self.source = SharingStrategy.share(source)
}
init(raw: Observable<Element>) {
self.source = raw
}
#if EXPANDABLE_SHARED_SEQUENCE
/**
This method is extension hook in case this unit needs to extended from outside the library.
By defining `EXPANDABLE_SHARED_SEQUENCE` one agrees that it's up to them to ensure shared sequence
properties are preserved after extension.
*/
public static func createUnsafe<Source: ObservableType>(source: Source) -> SharedSequence<SharingStrategy, Source.Element> {
SharedSequence<SharingStrategy, Source.Element>(raw: source.asObservable())
}
#endif
/**
- returns: Built observable sequence.
*/
public func asObservable() -> Observable<Element> {
self.source
}
/**
- returns: `self`
*/
public func asSharedSequence() -> SharedSequence<SharingStrategy, Element> {
self
}
}
/**
Different `SharedSequence` sharing strategies must conform to this protocol.
*/
public protocol SharingStrategyProtocol {
/**
Scheduled on which all sequence events will be delivered.
*/
static var scheduler: SchedulerType { get }
/**
Computation resources sharing strategy for multiple sequence observers.
E.g. One can choose `share(replay:scope:)`
as sequence event sharing strategies, but also do something more exotic, like
implementing promises or lazy loading chains.
*/
static func share<Element>(_ source: Observable<Element>) -> Observable<Element>
}
/**
A type that can be converted to `SharedSequence`.
*/
public protocol SharedSequenceConvertibleType : ObservableConvertibleType {
associatedtype SharingStrategy: SharingStrategyProtocol
/**
Converts self to `SharedSequence`.
*/
func asSharedSequence() -> SharedSequence<SharingStrategy, Element>
}
extension SharedSequenceConvertibleType {
public func asObservable() -> Observable<Element> {
self.asSharedSequence().asObservable()
}
}
extension SharedSequence {
/**
Returns an empty observable sequence, using the specified scheduler to send out the single `Completed` message.
- returns: An observable sequence with no elements.
*/
public static func empty() -> SharedSequence<SharingStrategy, Element> {
SharedSequence(raw: Observable.empty().subscribe(on: SharingStrategy.scheduler))
}
/**
Returns a non-terminating observable sequence, which can be used to denote an infinite duration.
- returns: An observable sequence whose observers will never get called.
*/
public static func never() -> SharedSequence<SharingStrategy, Element> {
SharedSequence(raw: Observable.never())
}
/**
Returns an observable sequence that contains a single element.
- parameter element: Single element in the resulting observable sequence.
- returns: An observable sequence containing the single specified element.
*/
public static func just(_ element: Element) -> SharedSequence<SharingStrategy, Element> {
SharedSequence(raw: Observable.just(element).subscribe(on: SharingStrategy.scheduler))
}
/**
Returns an observable sequence that invokes the specified factory function whenever a new observer subscribes.
- parameter observableFactory: Observable factory function to invoke for each observer that subscribes to the resulting sequence.
- returns: An observable sequence whose observers trigger an invocation of the given observable factory function.
*/
public static func deferred(_ observableFactory: @escaping () -> SharedSequence<SharingStrategy, Element>)
-> SharedSequence<SharingStrategy, Element> {
SharedSequence(Observable.deferred { observableFactory().asObservable() })
}
/**
This method creates a new Observable instance with a variable number of elements.
- seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html)
- parameter elements: Elements to generate.
- returns: The observable sequence whose elements are pulled from the given arguments.
*/
public static func of(_ elements: Element ...) -> SharedSequence<SharingStrategy, Element> {
let source = Observable.from(elements, scheduler: SharingStrategy.scheduler)
return SharedSequence(raw: source)
}
}
extension SharedSequence {
/**
This method converts an array to an observable sequence.
- seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html)
- returns: The observable sequence whose elements are pulled from the given enumerable sequence.
*/
public static func from(_ array: [Element]) -> SharedSequence<SharingStrategy, Element> {
let source = Observable.from(array, scheduler: SharingStrategy.scheduler)
return SharedSequence(raw: source)
}
/**
This method converts a sequence to an observable sequence.
- seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html)
- returns: The observable sequence whose elements are pulled from the given enumerable sequence.
*/
public static func from<Sequence: Swift.Sequence>(_ sequence: Sequence) -> SharedSequence<SharingStrategy, Element> where Sequence.Element == Element {
let source = Observable.from(sequence, scheduler: SharingStrategy.scheduler)
return SharedSequence(raw: source)
}
/**
This method converts a optional to an observable sequence.
- seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html)
- parameter optional: Optional element in the resulting observable sequence.
- returns: An observable sequence containing the wrapped value or not from given optional.
*/
public static func from(optional: Element?) -> SharedSequence<SharingStrategy, Element> {
let source = Observable.from(optional: optional, scheduler: SharingStrategy.scheduler)
return SharedSequence(raw: source)
}
}
extension SharedSequence where Element: RxAbstractInteger {
/**
Returns an observable sequence that produces a value after each period, using the specified scheduler to run timers and to send out observer messages.
- seealso: [interval operator on reactivex.io](http://reactivex.io/documentation/operators/interval.html)
- parameter period: Period for producing the values in the resulting sequence.
- returns: An observable sequence that produces a value after each period.
*/
public static func interval(_ period: RxTimeInterval)
-> SharedSequence<SharingStrategy, Element> {
SharedSequence(Observable.interval(period, scheduler: SharingStrategy.scheduler))
}
}
// MARK: timer
extension SharedSequence where Element: RxAbstractInteger {
/**
Returns an observable sequence that periodically produces a value after the specified initial relative due time has elapsed, using the specified scheduler to run timers.
- seealso: [timer operator on reactivex.io](http://reactivex.io/documentation/operators/timer.html)
- parameter dueTime: Relative time at which to produce the first value.
- parameter period: Period to produce subsequent values.
- returns: An observable sequence that produces a value after due time has elapsed and then each period.
*/
public static func timer(_ dueTime: RxTimeInterval, period: RxTimeInterval)
-> SharedSequence<SharingStrategy, Element> {
SharedSequence(Observable.timer(dueTime, period: period, scheduler: SharingStrategy.scheduler))
}
}

View File

@@ -0,0 +1,25 @@
//
// ControlEvent+Signal.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 11/1/17.
// Copyright © 2017 Krunoslav Zaher. All rights reserved.
//
import RxSwift
extension ControlEvent {
/// Converts `ControlEvent` to `Signal` trait.
///
/// `ControlEvent` already can't fail, so no special case needs to be handled.
public func asSignal() -> Signal<Element> {
return self.asSignal { _ -> Signal<Element> in
#if DEBUG
rxFatalError("Somehow signal received error from a source that shouldn't fail.")
#else
return Signal.empty()
#endif
}
}
}

View File

@@ -0,0 +1,57 @@
//
// ObservableConvertibleType+Signal.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 9/19/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import RxSwift
extension ObservableConvertibleType {
/**
Converts observable sequence to `Signal` trait.
- parameter onErrorJustReturn: Element to return in case of error and after that complete the sequence.
- returns: Signal trait.
*/
public func asSignal(onErrorJustReturn: Element) -> Signal<Element> {
let source = self
.asObservable()
.observe(on: SignalSharingStrategy.scheduler)
.catchAndReturn(onErrorJustReturn)
return Signal(source)
}
/**
Converts observable sequence to `Signal` trait.
- parameter onErrorSignalWith: Signal that continues to emit the sequence in case of error.
- returns: Signal trait.
*/
public func asSignal(onErrorSignalWith: Signal<Element>) -> Signal<Element> {
let source = self
.asObservable()
.observe(on: SignalSharingStrategy.scheduler)
.catch { _ in
onErrorSignalWith.asObservable()
}
return Signal(source)
}
/**
Converts observable sequence to `Signal` trait.
- parameter onErrorRecover: Calculates signal that continues to emit the sequence in case of error.
- returns: Signal trait.
*/
public func asSignal(onErrorRecover: @escaping (_ error: Swift.Error) -> Signal<Element>) -> Signal<Element> {
let source = self
.asObservable()
.observe(on: SignalSharingStrategy.scheduler)
.catch { error in
onErrorRecover(error).asObservable()
}
return Signal(source)
}
}

View File

@@ -0,0 +1,21 @@
//
// PublishRelay+Signal.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 12/28/15.
// Copyright © 2017 Krunoslav Zaher. All rights reserved.
//
import RxSwift
import RxRelay
extension PublishRelay {
/// Converts `PublishRelay` to `Signal`.
///
/// - returns: Observable sequence.
public func asSignal() -> Signal<Element> {
let source = self.asObservable()
.observe(on:SignalSharingStrategy.scheduler)
return SharedSequence(source)
}
}

View File

@@ -0,0 +1,178 @@
//
// Signal+Subscription.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 9/19/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import RxSwift
import RxRelay
extension SharedSequenceConvertibleType where SharingStrategy == SignalSharingStrategy {
/**
Creates new subscription and sends elements to observer.
In this form it's equivalent to `subscribe` method, but it communicates intent better.
- parameter observers: Observers that receives events.
- returns: Disposable object that can be used to unsubscribe the observer from the subject.
*/
public func emit<Observer: ObserverType>(to observers: Observer...) -> Disposable where Observer.Element == Element {
return self.asSharedSequence()
.asObservable()
.subscribe { event in
observers.forEach { $0.on(event) }
}
}
/**
Creates new subscription and sends elements to observer.
In this form it's equivalent to `subscribe` method, but it communicates intent better.
- parameter observers: Observers that receives events.
- returns: Disposable object that can be used to unsubscribe the observer from the subject.
*/
public func emit<Observer: ObserverType>(to observers: Observer...) -> Disposable where Observer.Element == Element? {
return self.asSharedSequence()
.asObservable()
.map { $0 as Element? }
.subscribe { event in
observers.forEach { $0.on(event) }
}
}
/**
Creates new subscription and sends elements to `BehaviorRelay`.
- parameter relays: Target relays for sequence elements.
- returns: Disposable object that can be used to unsubscribe the observer from the relay.
*/
public func emit(to relays: BehaviorRelay<Element>...) -> Disposable {
return self.emit(onNext: { e in
relays.forEach { $0.accept(e) }
})
}
/**
Creates new subscription and sends elements to `BehaviorRelay`.
- parameter relays: Target relays for sequence elements.
- returns: Disposable object that can be used to unsubscribe the observer from the relay.
*/
public func emit(to relays: BehaviorRelay<Element?>...) -> Disposable {
return self.emit(onNext: { e in
relays.forEach { $0.accept(e) }
})
}
/**
Creates new subscription and sends elements to `PublishRelay`.
- parameter relays: Target relays for sequence elements.
- returns: Disposable object that can be used to unsubscribe the observer from the relay.
*/
public func emit(to relays: PublishRelay<Element>...) -> Disposable {
return self.emit(onNext: { e in
relays.forEach { $0.accept(e) }
})
}
/**
Creates new subscription and sends elements to `PublishRelay`.
- parameter relays: Target relay for sequence elements.
- returns: Disposable object that can be used to unsubscribe the observer from the relay.
*/
public func emit(to relays: PublishRelay<Element?>...) -> Disposable {
return self.emit(onNext: { e in
relays.forEach { $0.accept(e) }
})
}
/**
Creates new subscription and sends elements to `ReplayRelay`.
- parameter relays: Target relays for sequence elements.
- returns: Disposable object that can be used to unsubscribe the observer from the relay.
*/
public func emit(to relays: ReplayRelay<Element>...) -> Disposable {
return self.emit(onNext: { e in
relays.forEach { $0.accept(e) }
})
}
/**
Creates new subscription and sends elements to `ReplayRelay`.
- parameter relays: Target relay for sequence elements.
- returns: Disposable object that can be used to unsubscribe the observer from the relay.
*/
public func emit(to relays: ReplayRelay<Element?>...) -> Disposable {
return self.emit(onNext: { e in
relays.forEach { $0.accept(e) }
})
}
/**
Subscribes an element handler, a completion handler and disposed handler to an observable sequence.
Also, take in an object and provide an unretained, safe to use (i.e. not implicitly unwrapped), reference to it along with the events emitted by the sequence.
Error callback is not exposed because `Signal` can't error out.
- Note: If `object` can't be retained, none of the other closures will be invoked.
- parameter object: The object to provide an unretained reference on.
- parameter onNext: Action to invoke for each element in the observable sequence.
- parameter onCompleted: Action to invoke upon graceful termination of the observable sequence.
gracefully completed, errored, or if the generation is canceled by disposing subscription)
- parameter onDisposed: Action to invoke upon any type of termination of sequence (if the sequence has
gracefully completed, errored, or if the generation is canceled by disposing subscription)
- returns: Subscription object used to unsubscribe from the observable sequence.
*/
public func emit<Object: AnyObject>(
with object: Object,
onNext: ((Object, Element) -> Void)? = nil,
onCompleted: ((Object) -> Void)? = nil,
onDisposed: ((Object) -> Void)? = nil
) -> Disposable {
self.asObservable().subscribe(
with: object,
onNext: onNext,
onCompleted: onCompleted,
onDisposed: onDisposed
)
}
/**
Subscribes an element handler, a completion handler and disposed handler to an observable sequence.
Error callback is not exposed because `Signal` can't error out.
- parameter onNext: Action to invoke for each element in the observable sequence.
- parameter onCompleted: Action to invoke upon graceful termination of the observable sequence.
gracefully completed, errored, or if the generation is canceled by disposing subscription)
- parameter onDisposed: Action to invoke upon any type of termination of sequence (if the sequence has
gracefully completed, errored, or if the generation is canceled by disposing subscription)
- returns: Subscription object used to unsubscribe from the observable sequence.
*/
public func emit(
onNext: ((Element) -> Void)? = nil,
onCompleted: (() -> Void)? = nil,
onDisposed: (() -> Void)? = nil
) -> Disposable {
self.asObservable().subscribe(onNext: onNext, onCompleted: onCompleted, onDisposed: onDisposed)
}
/**
Subscribes to this `Signal` with a no-op.
This method can be only called from `MainThread`.
- note: This is an alias of `emit(onNext: nil, onCompleted: nil, onDisposed: nil)` used to fix an ambiguity bug in Swift: https://bugs.swift.org/browse/SR-13657
- returns: Subscription object used to unsubscribe from the observable sequence.
*/
public func emit() -> Disposable {
emit(onNext: nil, onCompleted: nil, onDisposed: nil)
}
}

View File

@@ -0,0 +1,45 @@
//
// Signal.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 9/26/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import RxSwift
/**
Trait that represents observable sequence with following properties:
- it never fails
- it delivers events on `MainScheduler.instance`
- `share(scope: .whileConnected)` sharing strategy
Additional explanation:
- all observers share sequence computation resources
- there is no replaying of sequence elements on new observer subscription
- computation of elements is reference counted with respect to the number of observers
- if there are no subscribers, it will release sequence computation resources
In case trait that models state propagation is required, please check `Driver`.
`Signal<Element>` can be considered a builder pattern for observable sequences that model imperative events part of the application.
To find out more about units and how to use them, please visit `Documentation/Traits.md`.
*/
public typealias Signal<Element> = SharedSequence<SignalSharingStrategy, Element>
public struct SignalSharingStrategy: SharingStrategyProtocol {
public static var scheduler: SchedulerType { SharingScheduler.make() }
public static func share<Element>(_ source: Observable<Element>) -> Observable<Element> {
source.share(scope: .whileConnected)
}
}
extension SharedSequenceConvertibleType where SharingStrategy == SignalSharingStrategy {
/// Adds `asPublisher` to `SharingSequence` with `PublishSharingStrategy`.
public func asSignal() -> Signal<Element> {
self.asSharedSequence()
}
}

View File

@@ -0,0 +1,108 @@
//
// RxCollectionViewReactiveArrayDataSource.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 6/29/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
// objc monkey business
class _RxCollectionViewReactiveArrayDataSource
: NSObject
, UICollectionViewDataSource {
@objc(numberOfSectionsInCollectionView:)
func numberOfSections(in: UICollectionView) -> Int {
1
}
func _collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
0
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
_collectionView(collectionView, numberOfItemsInSection: section)
}
fileprivate func _collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
rxAbstractMethod()
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
_collectionView(collectionView, cellForItemAt: indexPath)
}
}
class RxCollectionViewReactiveArrayDataSourceSequenceWrapper<Sequence: Swift.Sequence>
: RxCollectionViewReactiveArrayDataSource<Sequence.Element>
, RxCollectionViewDataSourceType {
typealias Element = Sequence
override init(cellFactory: @escaping CellFactory) {
super.init(cellFactory: cellFactory)
}
func collectionView(_ collectionView: UICollectionView, observedEvent: Event<Sequence>) {
Binder(self) { collectionViewDataSource, sectionModels in
let sections = Array(sectionModels)
collectionViewDataSource.collectionView(collectionView, observedElements: sections)
}.on(observedEvent)
}
}
// Please take a look at `DelegateProxyType.swift`
class RxCollectionViewReactiveArrayDataSource<Element>
: _RxCollectionViewReactiveArrayDataSource
, SectionedViewDataSourceType {
typealias CellFactory = (UICollectionView, Int, Element) -> UICollectionViewCell
var itemModels: [Element]?
func modelAtIndex(_ index: Int) -> Element? {
itemModels?[index]
}
func model(at indexPath: IndexPath) throws -> Any {
precondition(indexPath.section == 0)
guard let item = itemModels?[indexPath.item] else {
throw RxCocoaError.itemsNotYetBound(object: self)
}
return item
}
var cellFactory: CellFactory
init(cellFactory: @escaping CellFactory) {
self.cellFactory = cellFactory
}
// data source
override func _collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
itemModels?.count ?? 0
}
override func _collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
cellFactory(collectionView, indexPath.item, itemModels![indexPath.item])
}
// reactive
func collectionView(_ collectionView: UICollectionView, observedElements: [Element]) {
self.itemModels = observedElements
collectionView.reloadData()
// workaround for http://stackoverflow.com/questions/39867325/ios-10-bug-uicollectionview-received-layout-attributes-for-a-cell-with-an-index
collectionView.collectionViewLayout.invalidateLayout()
}
}
#endif

View File

@@ -0,0 +1,92 @@
//
// RxPickerViewAdapter.swift
// RxCocoa
//
// Created by Sergey Shulga on 12/07/2017.
// Copyright © 2017 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import UIKit
import RxSwift
class RxPickerViewArrayDataSource<T>: NSObject, UIPickerViewDataSource, SectionedViewDataSourceType {
fileprivate var items: [T] = []
func model(at indexPath: IndexPath) throws -> Any {
guard items.indices ~= indexPath.row else {
throw RxCocoaError.itemsNotYetBound(object: self)
}
return items[indexPath.row]
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
items.count
}
}
class RxPickerViewSequenceDataSource<Sequence: Swift.Sequence>
: RxPickerViewArrayDataSource<Sequence.Element>
, RxPickerViewDataSourceType {
typealias Element = Sequence
func pickerView(_ pickerView: UIPickerView, observedEvent: Event<Sequence>) {
Binder(self) { dataSource, items in
dataSource.items = items
pickerView.reloadAllComponents()
}
.on(observedEvent.map(Array.init))
}
}
final class RxStringPickerViewAdapter<Sequence: Swift.Sequence>
: RxPickerViewSequenceDataSource<Sequence>
, UIPickerViewDelegate {
typealias TitleForRow = (Int, Sequence.Element) -> String?
private let titleForRow: TitleForRow
init(titleForRow: @escaping TitleForRow) {
self.titleForRow = titleForRow
super.init()
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
titleForRow(row, items[row])
}
}
final class RxAttributedStringPickerViewAdapter<Sequence: Swift.Sequence>: RxPickerViewSequenceDataSource<Sequence>, UIPickerViewDelegate {
typealias AttributedTitleForRow = (Int, Sequence.Element) -> NSAttributedString?
private let attributedTitleForRow: AttributedTitleForRow
init(attributedTitleForRow: @escaping AttributedTitleForRow) {
self.attributedTitleForRow = attributedTitleForRow
super.init()
}
func pickerView(_ pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
attributedTitleForRow(row, items[row])
}
}
final class RxPickerViewAdapter<Sequence: Swift.Sequence>: RxPickerViewSequenceDataSource<Sequence>, UIPickerViewDelegate {
typealias ViewForRow = (Int, Sequence.Element, UIView?) -> UIView
private let viewForRow: ViewForRow
init(viewForRow: @escaping ViewForRow) {
self.viewForRow = viewForRow
super.init()
}
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
viewForRow(row, items[row], view)
}
}
#endif

View File

@@ -0,0 +1,101 @@
//
// RxTableViewReactiveArrayDataSource.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 6/26/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
// objc monkey business
class _RxTableViewReactiveArrayDataSource
: NSObject
, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
1
}
func _tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
_tableView(tableView, numberOfRowsInSection: section)
}
fileprivate func _tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
rxAbstractMethod()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
_tableView(tableView, cellForRowAt: indexPath)
}
}
class RxTableViewReactiveArrayDataSourceSequenceWrapper<Sequence: Swift.Sequence>
: RxTableViewReactiveArrayDataSource<Sequence.Element>
, RxTableViewDataSourceType {
typealias Element = Sequence
override init(cellFactory: @escaping CellFactory) {
super.init(cellFactory: cellFactory)
}
func tableView(_ tableView: UITableView, observedEvent: Event<Sequence>) {
Binder(self) { tableViewDataSource, sectionModels in
let sections = Array(sectionModels)
tableViewDataSource.tableView(tableView, observedElements: sections)
}.on(observedEvent)
}
}
// Please take a look at `DelegateProxyType.swift`
class RxTableViewReactiveArrayDataSource<Element>
: _RxTableViewReactiveArrayDataSource
, SectionedViewDataSourceType {
typealias CellFactory = (UITableView, Int, Element) -> UITableViewCell
var itemModels: [Element]?
func modelAtIndex(_ index: Int) -> Element? {
itemModels?[index]
}
func model(at indexPath: IndexPath) throws -> Any {
precondition(indexPath.section == 0)
guard let item = itemModels?[indexPath.item] else {
throw RxCocoaError.itemsNotYetBound(object: self)
}
return item
}
let cellFactory: CellFactory
init(cellFactory: @escaping CellFactory) {
self.cellFactory = cellFactory
}
override func _tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
itemModels?.count ?? 0
}
override func _tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
cellFactory(tableView, indexPath.item, itemModels![indexPath.row])
}
// reactive
func tableView(_ tableView: UITableView, observedElements: [Element]) {
self.itemModels = observedElements
tableView.reloadData()
}
}
#endif

View File

@@ -0,0 +1,15 @@
//
// ItemEvents.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 6/20/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
public typealias ItemMovedEvent = (sourceIndex: IndexPath, destinationIndex: IndexPath)
public typealias WillDisplayCellEvent = (cell: UITableViewCell, indexPath: IndexPath)
public typealias DidEndDisplayingCellEvent = (cell: UITableViewCell, indexPath: IndexPath)
#endif

View File

@@ -0,0 +1,35 @@
//
// NSTextStorage+Rx.swift
// RxCocoa
//
// Created by Segii Shulga on 12/30/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import RxSwift
import UIKit
extension Reactive where Base: NSTextStorage {
/// Reactive wrapper for `delegate`.
///
/// For more information take a look at `DelegateProxyType` protocol documentation.
public var delegate: DelegateProxy<NSTextStorage, NSTextStorageDelegate> {
return RxTextStorageDelegateProxy.proxy(for: base)
}
/// Reactive wrapper for `delegate` message.
public var didProcessEditingRangeChangeInLength: Observable<(editedMask: NSTextStorage.EditActions, editedRange: NSRange, delta: Int)> {
return delegate
.methodInvoked(#selector(NSTextStorageDelegate.textStorage(_:didProcessEditing:range:changeInLength:)))
.map { a in
let editedMask = NSTextStorage.EditActions(rawValue: try castOrThrow(UInt.self, a[1]) )
let editedRange = try castOrThrow(NSValue.self, a[2]).rangeValue
let delta = try castOrThrow(Int.self, a[3])
return (editedMask, editedRange, delta)
}
}
}
#endif

View File

@@ -0,0 +1,27 @@
//
// RxCollectionViewDataSourceType.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 6/29/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
/// Marks data source as `UICollectionView` reactive data source enabling it to be used with one of the `bindTo` methods.
public protocol RxCollectionViewDataSourceType /*: UICollectionViewDataSource*/ {
/// Type of elements that can be bound to collection view.
associatedtype Element
/// New observable sequence event observed.
///
/// - parameter collectionView: Bound collection view.
/// - parameter observedEvent: Event
func collectionView(_ collectionView: UICollectionView, observedEvent: Event<Element>)
}
#endif

View File

@@ -0,0 +1,26 @@
//
// RxPickerViewDataSourceType.swift
// RxCocoa
//
// Created by Sergey Shulga on 05/07/2017.
// Copyright © 2017 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import UIKit
import RxSwift
/// 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

View File

@@ -0,0 +1,27 @@
//
// RxTableViewDataSourceType.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 6/26/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
/// Marks data source as `UITableView` reactive data source enabling it to be used with one of the `bindTo` methods.
public protocol RxTableViewDataSourceType /*: UITableViewDataSource*/ {
/// Type of elements that can be bound to table view.
associatedtype Element
/// New observable sequence event observed.
///
/// - parameter tableView: Bound table view.
/// - parameter observedEvent: Event
func tableView(_ tableView: UITableView, observedEvent: Event<Element>)
}
#endif

View File

@@ -0,0 +1,92 @@
//
// RxCollectionViewDataSourcePrefetchingProxy.swift
// RxCocoa
//
// Created by Rowan Livingstone on 2/15/18.
// Copyright © 2018 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
@available(iOS 10.0, tvOS 10.0, *)
extension UICollectionView: HasPrefetchDataSource {
public typealias PrefetchDataSource = UICollectionViewDataSourcePrefetching
}
@available(iOS 10.0, tvOS 10.0, *)
private let collectionViewPrefetchDataSourceNotSet = CollectionViewPrefetchDataSourceNotSet()
@available(iOS 10.0, tvOS 10.0, *)
private final class CollectionViewPrefetchDataSourceNotSet
: NSObject
, UICollectionViewDataSourcePrefetching {
func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {}
}
@available(iOS 10.0, tvOS 10.0, *)
open class RxCollectionViewDataSourcePrefetchingProxy
: DelegateProxy<UICollectionView, UICollectionViewDataSourcePrefetching>
, DelegateProxyType {
/// Typed parent object.
public weak private(set) var collectionView: UICollectionView?
/// - parameter collectionView: Parent object for delegate proxy.
public init(collectionView: ParentObject) {
self.collectionView = collectionView
super.init(parentObject: collectionView, delegateProxy: RxCollectionViewDataSourcePrefetchingProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { RxCollectionViewDataSourcePrefetchingProxy(collectionView: $0) }
}
private var _prefetchItemsPublishSubject: PublishSubject<[IndexPath]>?
/// Optimized version used for observing prefetch items callbacks.
internal var prefetchItemsPublishSubject: PublishSubject<[IndexPath]> {
if let subject = _prefetchItemsPublishSubject {
return subject
}
let subject = PublishSubject<[IndexPath]>()
_prefetchItemsPublishSubject = subject
return subject
}
private weak var _requiredMethodsPrefetchDataSource: UICollectionViewDataSourcePrefetching? = collectionViewPrefetchDataSourceNotSet
/// For more information take a look at `DelegateProxyType`.
open override func setForwardToDelegate(_ forwardToDelegate: UICollectionViewDataSourcePrefetching?, retainDelegate: Bool) {
_requiredMethodsPrefetchDataSource = forwardToDelegate ?? collectionViewPrefetchDataSourceNotSet
super.setForwardToDelegate(forwardToDelegate, retainDelegate: retainDelegate)
}
deinit {
if let subject = _prefetchItemsPublishSubject {
subject.on(.completed)
}
}
}
@available(iOS 10.0, tvOS 10.0, *)
extension RxCollectionViewDataSourcePrefetchingProxy: UICollectionViewDataSourcePrefetching {
/// Required delegate method implementation.
public func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
if let subject = _prefetchItemsPublishSubject {
subject.on(.next(indexPaths))
}
(_requiredMethodsPrefetchDataSource ?? collectionViewPrefetchDataSourceNotSet).collectionView(collectionView, prefetchItemsAt: indexPaths)
}
}
#endif

View File

@@ -0,0 +1,75 @@
//
// RxCollectionViewDataSourceProxy.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 6/29/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
extension UICollectionView: HasDataSource {
public typealias DataSource = UICollectionViewDataSource
}
private let collectionViewDataSourceNotSet = CollectionViewDataSourceNotSet()
private final class CollectionViewDataSourceNotSet
: NSObject
, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
0
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
rxAbstractMethod(message: dataSourceNotSet)
}
}
/// For more information take a look at `DelegateProxyType`.
open class RxCollectionViewDataSourceProxy
: DelegateProxy<UICollectionView, UICollectionViewDataSource>
, DelegateProxyType {
/// Typed parent object.
public weak private(set) var collectionView: UICollectionView?
/// - parameter collectionView: Parent object for delegate proxy.
public init(collectionView: ParentObject) {
self.collectionView = collectionView
super.init(parentObject: collectionView, delegateProxy: RxCollectionViewDataSourceProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { RxCollectionViewDataSourceProxy(collectionView: $0) }
}
private weak var _requiredMethodsDataSource: UICollectionViewDataSource? = collectionViewDataSourceNotSet
/// For more information take a look at `DelegateProxyType`.
open override func setForwardToDelegate(_ forwardToDelegate: UICollectionViewDataSource?, retainDelegate: Bool) {
_requiredMethodsDataSource = forwardToDelegate ?? collectionViewDataSourceNotSet
super.setForwardToDelegate(forwardToDelegate, retainDelegate: retainDelegate)
}
}
extension RxCollectionViewDataSourceProxy: UICollectionViewDataSource {
/// Required delegate method implementation.
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
(_requiredMethodsDataSource ?? collectionViewDataSourceNotSet).collectionView(collectionView, numberOfItemsInSection: section)
}
/// Required delegate method implementation.
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
(_requiredMethodsDataSource ?? collectionViewDataSourceNotSet).collectionView(collectionView, cellForItemAt: indexPath)
}
}
#endif

View File

@@ -0,0 +1,32 @@
//
// RxCollectionViewDelegateProxy.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 6/29/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
/// For more information take a look at `DelegateProxyType`.
open class RxCollectionViewDelegateProxy
: RxScrollViewDelegateProxy {
/// Typed parent object.
public weak private(set) var collectionView: UICollectionView?
/// Initializes `RxCollectionViewDelegateProxy`
///
/// - parameter collectionView: Parent object for delegate proxy.
public init(collectionView: UICollectionView) {
self.collectionView = collectionView
super.init(scrollView: collectionView)
}
}
extension RxCollectionViewDelegateProxy: UICollectionViewDelegateFlowLayout {}
#endif

View File

@@ -0,0 +1,39 @@
//
// RxNavigationControllerDelegateProxy.swift
// RxCocoa
//
// Created by Diogo on 13/04/17.
// Copyright © 2017 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
extension UINavigationController: HasDelegate {
public typealias Delegate = UINavigationControllerDelegate
}
/// For more information take a look at `DelegateProxyType`.
open class RxNavigationControllerDelegateProxy
: DelegateProxy<UINavigationController, UINavigationControllerDelegate>
, DelegateProxyType {
/// Typed parent object.
public weak private(set) var navigationController: UINavigationController?
/// - parameter navigationController: Parent object for delegate proxy.
public init(navigationController: ParentObject) {
self.navigationController = navigationController
super.init(parentObject: navigationController, delegateProxy: RxNavigationControllerDelegateProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { RxNavigationControllerDelegateProxy(navigationController: $0) }
}
}
extension RxNavigationControllerDelegateProxy: UINavigationControllerDelegate {}
#endif

View File

@@ -0,0 +1,72 @@
//
// RxPickerViewDataSourceProxy.swift
// RxCocoa
//
// Created by Sergey Shulga on 05/07/2017.
// Copyright © 2017 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import UIKit
import RxSwift
extension UIPickerView: HasDataSource {
public typealias DataSource = UIPickerViewDataSource
}
private let pickerViewDataSourceNotSet = PickerViewDataSourceNotSet()
final private class PickerViewDataSourceNotSet: NSObject, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
0
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
0
}
}
/// For more information take a look at `DelegateProxyType`.
public class RxPickerViewDataSourceProxy
: DelegateProxy<UIPickerView, UIPickerViewDataSource>
, DelegateProxyType {
/// Typed parent object.
public weak private(set) var pickerView: UIPickerView?
/// - parameter pickerView: Parent object for delegate proxy.
public init(pickerView: ParentObject) {
self.pickerView = pickerView
super.init(parentObject: pickerView, delegateProxy: RxPickerViewDataSourceProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { RxPickerViewDataSourceProxy(pickerView: $0) }
}
private weak var _requiredMethodsDataSource: UIPickerViewDataSource? = pickerViewDataSourceNotSet
/// For more information take a look at `DelegateProxyType`.
public override func setForwardToDelegate(_ forwardToDelegate: UIPickerViewDataSource?, retainDelegate: Bool) {
_requiredMethodsDataSource = forwardToDelegate ?? pickerViewDataSourceNotSet
super.setForwardToDelegate(forwardToDelegate, retainDelegate: retainDelegate)
}
}
// MARK: UIPickerViewDataSource
extension RxPickerViewDataSourceProxy: UIPickerViewDataSource {
/// Required delegate method implementation.
public func numberOfComponents(in pickerView: UIPickerView) -> Int {
(_requiredMethodsDataSource ?? pickerViewDataSourceNotSet).numberOfComponents(in: pickerView)
}
/// Required delegate method implementation.
public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
(_requiredMethodsDataSource ?? pickerViewDataSourceNotSet).pickerView(pickerView, numberOfRowsInComponent: component)
}
}
#endif

View File

@@ -0,0 +1,38 @@
//
// RxPickerViewDelegateProxy.swift
// RxCocoa
//
// Created by Segii Shulga on 5/12/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import RxSwift
import UIKit
extension UIPickerView: HasDelegate {
public typealias Delegate = UIPickerViewDelegate
}
open class RxPickerViewDelegateProxy
: DelegateProxy<UIPickerView, UIPickerViewDelegate>
, DelegateProxyType {
/// Typed parent object.
public weak private(set) var pickerView: UIPickerView?
/// - parameter pickerView: Parent object for delegate proxy.
public init(pickerView: ParentObject) {
self.pickerView = pickerView
super.init(parentObject: pickerView, delegateProxy: RxPickerViewDelegateProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { RxPickerViewDelegateProxy(pickerView: $0) }
}
}
extension RxPickerViewDelegateProxy: UIPickerViewDelegate {}
#endif

View File

@@ -0,0 +1,91 @@
//
// RxScrollViewDelegateProxy.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 6/19/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import RxSwift
import UIKit
extension UIScrollView: HasDelegate {
public typealias Delegate = UIScrollViewDelegate
}
/// For more information take a look at `DelegateProxyType`.
open class RxScrollViewDelegateProxy
: DelegateProxy<UIScrollView, UIScrollViewDelegate>
, DelegateProxyType {
/// Typed parent object.
public weak private(set) var scrollView: UIScrollView?
/// - parameter scrollView: Parent object for delegate proxy.
public init(scrollView: ParentObject) {
self.scrollView = scrollView
super.init(parentObject: scrollView, delegateProxy: RxScrollViewDelegateProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { RxScrollViewDelegateProxy(scrollView: $0) }
self.register { RxTableViewDelegateProxy(tableView: $0) }
self.register { RxCollectionViewDelegateProxy(collectionView: $0) }
self.register { RxTextViewDelegateProxy(textView: $0) }
}
private var _contentOffsetBehaviorSubject: BehaviorSubject<CGPoint>?
private var _contentOffsetPublishSubject: PublishSubject<()>?
/// Optimized version used for observing content offset changes.
internal var contentOffsetBehaviorSubject: BehaviorSubject<CGPoint> {
if let subject = _contentOffsetBehaviorSubject {
return subject
}
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
}
deinit {
if let subject = _contentOffsetBehaviorSubject {
subject.on(.completed)
}
if let subject = _contentOffsetPublishSubject {
subject.on(.completed)
}
}
}
extension RxScrollViewDelegateProxy: UIScrollViewDelegate {
/// For more information take a look at `DelegateProxyType`.
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if let subject = _contentOffsetBehaviorSubject {
subject.on(.next(scrollView.contentOffset))
}
if let subject = _contentOffsetPublishSubject {
subject.on(.next(()))
}
self._forwardToDelegate?.scrollViewDidScroll?(scrollView)
}
}
#endif

View File

@@ -0,0 +1,40 @@
//
// RxSearchBarDelegateProxy.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 7/4/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
extension UISearchBar: HasDelegate {
public typealias Delegate = UISearchBarDelegate
}
/// For more information take a look at `DelegateProxyType`.
open class RxSearchBarDelegateProxy
: DelegateProxy<UISearchBar, UISearchBarDelegate>
, DelegateProxyType {
/// Typed parent object.
public weak private(set) var searchBar: UISearchBar?
/// - parameter searchBar: Parent object for delegate proxy.
public init(searchBar: ParentObject) {
self.searchBar = searchBar
super.init(parentObject: searchBar, delegateProxy: RxSearchBarDelegateProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { RxSearchBarDelegateProxy(searchBar: $0) }
}
}
extension RxSearchBarDelegateProxy: UISearchBarDelegate {}
#endif

View File

@@ -0,0 +1,40 @@
//
// RxSearchControllerDelegateProxy.swift
// RxCocoa
//
// Created by Segii Shulga on 3/17/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import RxSwift
import UIKit
extension UISearchController: HasDelegate {
public typealias Delegate = UISearchControllerDelegate
}
/// For more information take a look at `DelegateProxyType`.
open class RxSearchControllerDelegateProxy
: DelegateProxy<UISearchController, UISearchControllerDelegate>
, DelegateProxyType {
/// Typed parent object.
public weak private(set) var searchController: UISearchController?
/// - parameter searchController: Parent object for delegate proxy.
public init(searchController: UISearchController) {
self.searchController = searchController
super.init(parentObject: searchController, delegateProxy: RxSearchControllerDelegateProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { RxSearchControllerDelegateProxy(searchController: $0) }
}
}
extension RxSearchControllerDelegateProxy: UISearchControllerDelegate {}
#endif

View File

@@ -0,0 +1,40 @@
//
// RxTabBarControllerDelegateProxy.swift
// RxCocoa
//
// Created by Yusuke Kita on 2016/12/07.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
extension UITabBarController: HasDelegate {
public typealias Delegate = UITabBarControllerDelegate
}
/// For more information take a look at `DelegateProxyType`.
open class RxTabBarControllerDelegateProxy
: DelegateProxy<UITabBarController, UITabBarControllerDelegate>
, DelegateProxyType {
/// Typed parent object.
public weak private(set) var tabBar: UITabBarController?
/// - parameter tabBar: Parent object for delegate proxy.
public init(tabBar: ParentObject) {
self.tabBar = tabBar
super.init(parentObject: tabBar, delegateProxy: RxTabBarControllerDelegateProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { RxTabBarControllerDelegateProxy(tabBar: $0) }
}
}
extension RxTabBarControllerDelegateProxy: UITabBarControllerDelegate {}
#endif

View File

@@ -0,0 +1,50 @@
//
// RxTabBarDelegateProxy.swift
// RxCocoa
//
// Created by Jesse Farless on 5/14/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
extension UITabBar: HasDelegate {
public typealias Delegate = UITabBarDelegate
}
/// For more information take a look at `DelegateProxyType`.
open class RxTabBarDelegateProxy
: DelegateProxy<UITabBar, UITabBarDelegate>
, DelegateProxyType {
/// Typed parent object.
public weak private(set) var tabBar: UITabBar?
/// - parameter tabBar: Parent object for delegate proxy.
public init(tabBar: ParentObject) {
self.tabBar = tabBar
super.init(parentObject: tabBar, delegateProxy: RxTabBarDelegateProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { RxTabBarDelegateProxy(tabBar: $0) }
}
/// For more information take a look at `DelegateProxyType`.
open class func currentDelegate(for object: ParentObject) -> UITabBarDelegate? {
object.delegate
}
/// For more information take a look at `DelegateProxyType`.
open class func setCurrentDelegate(_ delegate: UITabBarDelegate?, to object: ParentObject) {
object.delegate = delegate
}
}
extension RxTabBarDelegateProxy: UITabBarDelegate {}
#endif

View File

@@ -0,0 +1,93 @@
//
// RxTableViewDataSourcePrefetchingProxy.swift
// RxCocoa
//
// Created by Rowan Livingstone on 2/15/18.
// Copyright © 2018 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
@available(iOS 10.0, tvOS 10.0, *)
extension UITableView: HasPrefetchDataSource {
public typealias PrefetchDataSource = UITableViewDataSourcePrefetching
}
@available(iOS 10.0, tvOS 10.0, *)
private let tableViewPrefetchDataSourceNotSet = TableViewPrefetchDataSourceNotSet()
@available(iOS 10.0, tvOS 10.0, *)
private final class TableViewPrefetchDataSourceNotSet
: NSObject
, UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {}
}
@available(iOS 10.0, tvOS 10.0, *)
open class RxTableViewDataSourcePrefetchingProxy
: DelegateProxy<UITableView, UITableViewDataSourcePrefetching>
, DelegateProxyType {
/// Typed parent object.
public weak private(set) var tableView: UITableView?
/// - parameter tableView: Parent object for delegate proxy.
public init(tableView: ParentObject) {
self.tableView = tableView
super.init(parentObject: tableView, delegateProxy: RxTableViewDataSourcePrefetchingProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { RxTableViewDataSourcePrefetchingProxy(tableView: $0) }
}
private var _prefetchRowsPublishSubject: PublishSubject<[IndexPath]>?
/// Optimized version used for observing prefetch rows callbacks.
internal var prefetchRowsPublishSubject: PublishSubject<[IndexPath]> {
if let subject = _prefetchRowsPublishSubject {
return subject
}
let subject = PublishSubject<[IndexPath]>()
_prefetchRowsPublishSubject = subject
return subject
}
private weak var _requiredMethodsPrefetchDataSource: UITableViewDataSourcePrefetching? = tableViewPrefetchDataSourceNotSet
/// For more information take a look at `DelegateProxyType`.
open override func setForwardToDelegate(_ forwardToDelegate: UITableViewDataSourcePrefetching?, retainDelegate: Bool) {
_requiredMethodsPrefetchDataSource = forwardToDelegate ?? tableViewPrefetchDataSourceNotSet
super.setForwardToDelegate(forwardToDelegate, retainDelegate: retainDelegate)
}
deinit {
if let subject = _prefetchRowsPublishSubject {
subject.on(.completed)
}
}
}
@available(iOS 10.0, tvOS 10.0, *)
extension RxTableViewDataSourcePrefetchingProxy: UITableViewDataSourcePrefetching {
/// Required delegate method implementation.
public func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
if let subject = _prefetchRowsPublishSubject {
subject.on(.next(indexPaths))
}
(_requiredMethodsPrefetchDataSource ?? tableViewPrefetchDataSourceNotSet).tableView(tableView, prefetchRowsAt: indexPaths)
}
}
#endif

View File

@@ -0,0 +1,73 @@
//
// RxTableViewDataSourceProxy.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 6/15/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
extension UITableView: HasDataSource {
public typealias DataSource = UITableViewDataSource
}
private let tableViewDataSourceNotSet = TableViewDataSourceNotSet()
private final class TableViewDataSourceNotSet
: NSObject
, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
rxAbstractMethod(message: dataSourceNotSet)
}
}
/// For more information take a look at `DelegateProxyType`.
open class RxTableViewDataSourceProxy
: DelegateProxy<UITableView, UITableViewDataSource>
, DelegateProxyType {
/// Typed parent object.
public weak private(set) var tableView: UITableView?
/// - parameter tableView: Parent object for delegate proxy.
public init(tableView: UITableView) {
self.tableView = tableView
super.init(parentObject: tableView, delegateProxy: RxTableViewDataSourceProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { RxTableViewDataSourceProxy(tableView: $0) }
}
private weak var _requiredMethodsDataSource: UITableViewDataSource? = tableViewDataSourceNotSet
/// For more information take a look at `DelegateProxyType`.
open override func setForwardToDelegate(_ forwardToDelegate: UITableViewDataSource?, retainDelegate: Bool) {
_requiredMethodsDataSource = forwardToDelegate ?? tableViewDataSourceNotSet
super.setForwardToDelegate(forwardToDelegate, retainDelegate: retainDelegate)
}
}
extension RxTableViewDataSourceProxy: UITableViewDataSource {
/// Required delegate method implementation.
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
(_requiredMethodsDataSource ?? tableViewDataSourceNotSet).tableView(tableView, numberOfRowsInSection: section)
}
/// Required delegate method implementation.
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
(_requiredMethodsDataSource ?? tableViewDataSourceNotSet).tableView(tableView, cellForRowAt: indexPath)
}
}
#endif

View File

@@ -0,0 +1,31 @@
//
// RxTableViewDelegateProxy.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 6/15/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
/// For more information take a look at `DelegateProxyType`.
open class RxTableViewDelegateProxy
: RxScrollViewDelegateProxy {
/// Typed parent object.
public weak private(set) var tableView: UITableView?
/// - parameter tableView: Parent object for delegate proxy.
public init(tableView: UITableView) {
self.tableView = tableView
super.init(scrollView: tableView)
}
}
extension RxTableViewDelegateProxy: UITableViewDelegate {}
#endif

View File

@@ -0,0 +1,38 @@
//
// RxTextStorageDelegateProxy.swift
// RxCocoa
//
// Created by Segii Shulga on 12/30/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import RxSwift
import UIKit
extension NSTextStorage: HasDelegate {
public typealias Delegate = NSTextStorageDelegate
}
open class RxTextStorageDelegateProxy
: DelegateProxy<NSTextStorage, NSTextStorageDelegate>
, DelegateProxyType {
/// Typed parent object.
public weak private(set) var textStorage: NSTextStorage?
/// - parameter textStorage: Parent object for delegate proxy.
public init(textStorage: NSTextStorage) {
self.textStorage = textStorage
super.init(parentObject: textStorage, delegateProxy: RxTextStorageDelegateProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { RxTextStorageDelegateProxy(textStorage: $0) }
}
}
extension RxTextStorageDelegateProxy: NSTextStorageDelegate {}
#endif

View File

@@ -0,0 +1,42 @@
//
// RxTextViewDelegateProxy.swift
// RxCocoa
//
// Created by Yuta ToKoRo on 7/19/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
/// For more information take a look at `DelegateProxyType`.
open class RxTextViewDelegateProxy
: RxScrollViewDelegateProxy {
/// Typed parent object.
public weak private(set) var textView: UITextView?
/// - parameter textview: Parent object for delegate proxy.
public init(textView: UITextView) {
self.textView = textView
super.init(scrollView: textView)
}
}
extension RxTextViewDelegateProxy: UITextViewDelegate {
/// For more information take a look at `DelegateProxyType`.
@objc open func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
/**
We've had some issues with observing text changes. This is here just in case we need the same hack in future and that
we wouldn't need to change the public interface.
*/
let forwardToDelegate = self.forwardToDelegate() as? UITextViewDelegate
return forwardToDelegate?.textView?(textView,
shouldChangeTextIn: range,
replacementText: text) ?? true
}
}
#endif

View File

@@ -0,0 +1,45 @@
//
// RxWKNavigationDelegateProxy.swift
// RxCocoa
//
// Created by Giuseppe Lanza on 14/02/2020.
// Copyright © 2020 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(macOS)
import RxSwift
import WebKit
@available(iOS 8.0, OSX 10.10, OSXApplicationExtension 10.10, *)
open class RxWKNavigationDelegateProxy
: DelegateProxy<WKWebView, WKNavigationDelegate>
, DelegateProxyType {
/// Typed parent object.
public weak private(set) var webView: WKWebView?
/// - parameter webView: Parent object for delegate proxy.
public init(webView: ParentObject) {
self.webView = webView
super.init(parentObject: webView, delegateProxy: RxWKNavigationDelegateProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { RxWKNavigationDelegateProxy(webView: $0) }
}
public static func currentDelegate(for object: WKWebView) -> WKNavigationDelegate? {
object.navigationDelegate
}
public static func setCurrentDelegate(_ delegate: WKNavigationDelegate?, to object: WKWebView) {
object.navigationDelegate = delegate
}
}
@available(iOS 8.0, OSX 10.10, OSXApplicationExtension 10.10, *)
extension RxWKNavigationDelegateProxy: WKNavigationDelegate {}
#endif

View File

@@ -0,0 +1,27 @@
//
// UIActivityIndicatorView+Rx.swift
// RxCocoa
//
// Created by Ivan Persidskiy on 02/12/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
extension Reactive where Base: UIActivityIndicatorView {
/// Bindable sink for `startAnimating()`, `stopAnimating()` methods.
public var isAnimating: Binder<Bool> {
Binder(self.base) { activityIndicator, active in
if active {
activityIndicator.startAnimating()
} else {
activityIndicator.stopAnimating()
}
}
}
}
#endif

View File

@@ -0,0 +1,106 @@
//
// UIApplication+Rx.swift
// RxCocoa
//
// Created by Mads Bøgeskov on 18/01/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import UIKit
import RxSwift
extension Reactive where Base: UIApplication {
/// Bindable sink for `isNetworkActivityIndicatorVisible`.
public var isNetworkActivityIndicatorVisible: Binder<Bool> {
return Binder(self.base) { application, active in
application.isNetworkActivityIndicatorVisible = active
}
}
/// Reactive wrapper for `UIApplication.didEnterBackgroundNotification`
public static var didEnterBackground: ControlEvent<Void> {
let source = NotificationCenter.default.rx.notification(UIApplication.didEnterBackgroundNotification).map { _ in }
return ControlEvent(events: source)
}
/// Reactive wrapper for `UIApplication.willEnterForegroundNotification`
public static var willEnterForeground: ControlEvent<Void> {
let source = NotificationCenter.default.rx.notification(UIApplication.willEnterForegroundNotification).map { _ in }
return ControlEvent(events: source)
}
/// Reactive wrapper for `UIApplication.didFinishLaunchingNotification`
public static var didFinishLaunching: ControlEvent<Void> {
let source = NotificationCenter.default.rx.notification(UIApplication.didFinishLaunchingNotification).map { _ in }
return ControlEvent(events: source)
}
/// Reactive wrapper for `UIApplication.didBecomeActiveNotification`
public static var didBecomeActive: ControlEvent<Void> {
let source = NotificationCenter.default.rx.notification(UIApplication.didBecomeActiveNotification).map { _ in }
return ControlEvent(events: source)
}
/// Reactive wrapper for `UIApplication.willResignActiveNotification`
public static var willResignActive: ControlEvent<Void> {
let source = NotificationCenter.default.rx.notification(UIApplication.willResignActiveNotification).map { _ in }
return ControlEvent(events: source)
}
/// Reactive wrapper for `UIApplication.didReceiveMemoryWarningNotification`
public static var didReceiveMemoryWarning: ControlEvent<Void> {
let source = NotificationCenter.default.rx.notification(UIApplication.didReceiveMemoryWarningNotification).map { _ in }
return ControlEvent(events: source)
}
/// Reactive wrapper for `UIApplication.willTerminateNotification`
public static var willTerminate: ControlEvent<Void> {
let source = NotificationCenter.default.rx.notification(UIApplication.willTerminateNotification).map { _ in }
return ControlEvent(events: source)
}
/// Reactive wrapper for `UIApplication.significantTimeChangeNotification`
public static var significantTimeChange: ControlEvent<Void> {
let source = NotificationCenter.default.rx.notification(UIApplication.significantTimeChangeNotification).map { _ in }
return ControlEvent(events: source)
}
/// Reactive wrapper for `UIApplication.backgroundRefreshStatusDidChangeNotification`
public static var backgroundRefreshStatusDidChange: ControlEvent<Void> {
let source = NotificationCenter.default.rx.notification(UIApplication.backgroundRefreshStatusDidChangeNotification).map { _ in }
return ControlEvent(events: source)
}
/// Reactive wrapper for `UIApplication.protectedDataWillBecomeUnavailableNotification`
public static var protectedDataWillBecomeUnavailable: ControlEvent<Void> {
let source = NotificationCenter.default.rx.notification(UIApplication.protectedDataWillBecomeUnavailableNotification).map { _ in }
return ControlEvent(events: source)
}
/// Reactive wrapper for `UIApplication.protectedDataDidBecomeAvailableNotification`
public static var protectedDataDidBecomeAvailable: ControlEvent<Void> {
let source = NotificationCenter.default.rx.notification(UIApplication.protectedDataDidBecomeAvailableNotification).map { _ in }
return ControlEvent(events: source)
}
/// Reactive wrapper for `UIApplication.userDidTakeScreenshotNotification`
public static var userDidTakeScreenshot: ControlEvent<Void> {
let source = NotificationCenter.default.rx.notification(UIApplication.userDidTakeScreenshotNotification).map { _ in }
return ControlEvent(events: source)
}
}
#endif

View File

@@ -0,0 +1,72 @@
//
// UIBarButtonItem+Rx.swift
// RxCocoa
//
// Created by Daniel Tartaglia on 5/31/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
private var rx_tap_key: UInt8 = 0
extension Reactive where Base: UIBarButtonItem {
/// Reactive wrapper for target action pattern on `self`.
public var tap: ControlEvent<()> {
let source = lazyInstanceObservable(&rx_tap_key) { () -> Observable<()> in
Observable.create { [weak control = self.base] observer in
guard let control = control else {
observer.on(.completed)
return Disposables.create()
}
let target = BarButtonItemTarget(barButtonItem: control) {
observer.on(.next(()))
}
return target
}
.take(until: self.deallocated)
.share()
}
return ControlEvent(events: source)
}
}
@objc
final class BarButtonItemTarget: RxTarget {
typealias Callback = () -> Void
weak var barButtonItem: UIBarButtonItem?
var callback: Callback!
init(barButtonItem: UIBarButtonItem, callback: @escaping () -> Void) {
self.barButtonItem = barButtonItem
self.callback = callback
super.init()
barButtonItem.target = self
barButtonItem.action = #selector(BarButtonItemTarget.action(_:))
}
override func dispose() {
super.dispose()
#if DEBUG
MainScheduler.ensureRunningOnMainThread()
#endif
barButtonItem?.target = nil
barButtonItem?.action = nil
callback = nil
}
@objc func action(_ sender: AnyObject) {
callback()
}
}
#endif

View File

@@ -0,0 +1,82 @@
//
// UIButton+Rx.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 3/28/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import RxSwift
import UIKit
extension Reactive where Base: UIButton {
/// Reactive wrapper for `TouchUpInside` control event.
public var tap: ControlEvent<Void> {
controlEvent(.touchUpInside)
}
}
#endif
#if os(tvOS)
import RxSwift
import UIKit
extension Reactive where Base: UIButton {
/// Reactive wrapper for `PrimaryActionTriggered` control event.
public var primaryAction: ControlEvent<Void> {
controlEvent(.primaryActionTriggered)
}
}
#endif
#if os(iOS) || os(tvOS)
import RxSwift
import UIKit
extension Reactive where Base: UIButton {
/// Reactive wrapper for `setTitle(_:for:)`
public func title(for controlState: UIControl.State = []) -> Binder<String?> {
Binder(self.base) { button, title in
button.setTitle(title, for: controlState)
}
}
/// Reactive wrapper for `setImage(_:for:)`
public func image(for controlState: UIControl.State = []) -> Binder<UIImage?> {
Binder(self.base) { button, image in
button.setImage(image, for: controlState)
}
}
/// Reactive wrapper for `setBackgroundImage(_:for:)`
public func backgroundImage(for controlState: UIControl.State = []) -> Binder<UIImage?> {
Binder(self.base) { button, image in
button.setBackgroundImage(image, for: controlState)
}
}
}
#endif
#if os(iOS) || os(tvOS)
import RxSwift
import UIKit
extension Reactive where Base: UIButton {
/// Reactive wrapper for `setAttributedTitle(_:controlState:)`
public func attributedTitle(for controlState: UIControl.State = []) -> Binder<NSAttributedString?> {
return Binder(self.base) { button, attributedTitle -> Void in
button.setAttributedTitle(attributedTitle, for: controlState)
}
}
}
#endif

View File

@@ -0,0 +1,380 @@
//
// UICollectionView+Rx.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 4/2/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import RxSwift
import UIKit
// Items
extension Reactive where Base: UICollectionView {
/**
Binds sequences of elements to collection view items.
- parameter source: Observable sequence of items.
- parameter cellFactory: Transform between sequence elements and view cells.
- returns: Disposable object that can be used to unbind.
Example
let items = Observable.just([
1,
2,
3
])
items
.bind(to: collectionView.rx.items) { (collectionView, row, element) in
let indexPath = IndexPath(row: row, section: 0)
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! NumberCell
cell.value?.text = "\(element) @ \(row)"
return cell
}
.disposed(by: disposeBag)
*/
public func items<Sequence: Swift.Sequence, Source: ObservableType>
(_ source: Source)
-> (_ cellFactory: @escaping (UICollectionView, Int, Sequence.Element) -> UICollectionViewCell)
-> Disposable where Source.Element == Sequence {
return { cellFactory in
let dataSource = RxCollectionViewReactiveArrayDataSourceSequenceWrapper<Sequence>(cellFactory: cellFactory)
return self.items(dataSource: dataSource)(source)
}
}
/**
Binds sequences of elements to collection view items.
- parameter cellIdentifier: Identifier used to dequeue cells.
- parameter source: Observable sequence of items.
- parameter configureCell: Transform between sequence elements and view cells.
- parameter cellType: Type of collection view cell.
- returns: Disposable object that can be used to unbind.
Example
let items = Observable.just([
1,
2,
3
])
items
.bind(to: collectionView.rx.items(cellIdentifier: "Cell", cellType: NumberCell.self)) { (row, element, cell) in
cell.value?.text = "\(element) @ \(row)"
}
.disposed(by: disposeBag)
*/
public func items<Sequence: Swift.Sequence, Cell: UICollectionViewCell, Source: ObservableType>
(cellIdentifier: String, cellType: Cell.Type = Cell.self)
-> (_ source: Source)
-> (_ configureCell: @escaping (Int, Sequence.Element, Cell) -> Void)
-> Disposable where Source.Element == Sequence {
return { source in
return { configureCell in
let dataSource = RxCollectionViewReactiveArrayDataSourceSequenceWrapper<Sequence> { cv, i, item in
let indexPath = IndexPath(item: i, section: 0)
let cell = cv.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! Cell
configureCell(i, item, cell)
return cell
}
return self.items(dataSource: dataSource)(source)
}
}
}
/**
Binds sequences of elements to collection view items using a custom reactive data used to perform the transformation.
- parameter dataSource: Data source used to transform elements to view cells.
- parameter source: Observable sequence of items.
- returns: Disposable object that can be used to unbind.
Example
let dataSource = RxCollectionViewSectionedReloadDataSource<SectionModel<String, Double>>()
let items = Observable.just([
SectionModel(model: "First section", items: [
1.0,
2.0,
3.0
]),
SectionModel(model: "Second section", items: [
1.0,
2.0,
3.0
]),
SectionModel(model: "Third section", items: [
1.0,
2.0,
3.0
])
])
dataSource.configureCell = { (dataSource, cv, indexPath, element) in
let cell = cv.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! NumberCell
cell.value?.text = "\(element) @ row \(indexPath.row)"
return cell
}
items
.bind(to: collectionView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
*/
public func items<
DataSource: RxCollectionViewDataSourceType & UICollectionViewDataSource,
Source: ObservableType>
(dataSource: DataSource)
-> (_ source: Source)
-> Disposable where DataSource.Element == Source.Element
{
return { source in
// This is called for side effects only, and to make sure delegate proxy is in place when
// data source is being bound.
// This is needed because theoretically the data source subscription itself might
// call `self.rx.delegate`. If that happens, it might cause weird side effects since
// setting data source will set delegate, and UICollectionView might get into a weird state.
// Therefore it's better to set delegate proxy first, just to be sure.
_ = self.delegate
// Strong reference is needed because data source is in use until result subscription is disposed
return source.subscribeProxyDataSource(ofObject: self.base, dataSource: dataSource, retainDataSource: true) { [weak collectionView = self.base] (_: RxCollectionViewDataSourceProxy, event) -> Void in
guard let collectionView = collectionView else {
return
}
dataSource.collectionView(collectionView, observedEvent: event)
}
}
}
}
extension Reactive where Base: UICollectionView {
public typealias DisplayCollectionViewCellEvent = (cell: UICollectionViewCell, at: IndexPath)
public typealias DisplayCollectionViewSupplementaryViewEvent = (supplementaryView: UICollectionReusableView, elementKind: String, at: IndexPath)
/// Reactive wrapper for `dataSource`.
///
/// For more information take a look at `DelegateProxyType` protocol documentation.
public var dataSource: DelegateProxy<UICollectionView, UICollectionViewDataSource> {
RxCollectionViewDataSourceProxy.proxy(for: base)
}
/// Installs data source as forwarding delegate on `rx.dataSource`.
/// Data source won't be retained.
///
/// It enables using normal delegate mechanism with reactive delegate mechanism.
///
/// - parameter dataSource: Data source object.
/// - returns: Disposable object that can be used to unbind the data source.
public func setDataSource(_ dataSource: UICollectionViewDataSource)
-> Disposable {
RxCollectionViewDataSourceProxy.installForwardDelegate(dataSource, retainDelegate: false, onProxyForObject: self.base)
}
/// Reactive wrapper for `delegate` message `collectionView(_:didSelectItemAtIndexPath:)`.
public var itemSelected: ControlEvent<IndexPath> {
let source = delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didSelectItemAt:)))
.map { a in
return try castOrThrow(IndexPath.self, a[1])
}
return ControlEvent(events: source)
}
/// Reactive wrapper for `delegate` message `collectionView(_:didDeselectItemAtIndexPath:)`.
public var itemDeselected: ControlEvent<IndexPath> {
let source = delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didDeselectItemAt:)))
.map { a in
return try castOrThrow(IndexPath.self, a[1])
}
return ControlEvent(events: source)
}
/// Reactive wrapper for `delegate` message `collectionView(_:didHighlightItemAt:)`.
public var itemHighlighted: ControlEvent<IndexPath> {
let source = delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didHighlightItemAt:)))
.map { a in
return try castOrThrow(IndexPath.self, a[1])
}
return ControlEvent(events: source)
}
/// Reactive wrapper for `delegate` message `collectionView(_:didUnhighlightItemAt:)`.
public var itemUnhighlighted: ControlEvent<IndexPath> {
let source = delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didUnhighlightItemAt:)))
.map { a in
return try castOrThrow(IndexPath.self, a[1])
}
return ControlEvent(events: source)
}
/// Reactive wrapper for `delegate` message `collectionView:willDisplay:forItemAt:`.
public var willDisplayCell: ControlEvent<DisplayCollectionViewCellEvent> {
let source: Observable<DisplayCollectionViewCellEvent> = self.delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:willDisplay:forItemAt:)))
.map { a in
return (try castOrThrow(UICollectionViewCell.self, a[1]), try castOrThrow(IndexPath.self, a[2]))
}
return ControlEvent(events: source)
}
/// Reactive wrapper for `delegate` message `collectionView(_:willDisplaySupplementaryView:forElementKind:at:)`.
public var willDisplaySupplementaryView: ControlEvent<DisplayCollectionViewSupplementaryViewEvent> {
let source: Observable<DisplayCollectionViewSupplementaryViewEvent> = self.delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:willDisplaySupplementaryView:forElementKind:at:)))
.map { a in
return (try castOrThrow(UICollectionReusableView.self, a[1]),
try castOrThrow(String.self, a[2]),
try castOrThrow(IndexPath.self, a[3]))
}
return ControlEvent(events: source)
}
/// Reactive wrapper for `delegate` message `collectionView:didEndDisplaying:forItemAt:`.
public var didEndDisplayingCell: ControlEvent<DisplayCollectionViewCellEvent> {
let source: Observable<DisplayCollectionViewCellEvent> = self.delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didEndDisplaying:forItemAt:)))
.map { a in
return (try castOrThrow(UICollectionViewCell.self, a[1]), try castOrThrow(IndexPath.self, a[2]))
}
return ControlEvent(events: source)
}
/// Reactive wrapper for `delegate` message `collectionView(_:didEndDisplayingSupplementaryView:forElementOfKind:at:)`.
public var didEndDisplayingSupplementaryView: ControlEvent<DisplayCollectionViewSupplementaryViewEvent> {
let source: Observable<DisplayCollectionViewSupplementaryViewEvent> = self.delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didEndDisplayingSupplementaryView:forElementOfKind:at:)))
.map { a in
return (try castOrThrow(UICollectionReusableView.self, a[1]),
try castOrThrow(String.self, a[2]),
try castOrThrow(IndexPath.self, a[3]))
}
return ControlEvent(events: source)
}
/// Reactive wrapper for `delegate` message `collectionView(_:didSelectItemAtIndexPath:)`.
///
/// It can be only used when one of the `rx.itemsWith*` methods is used to bind observable sequence,
/// or any other data source conforming to `SectionedViewDataSourceType` protocol.
///
/// ```
/// collectionView.rx.modelSelected(MyModel.self)
/// .map { ...
/// ```
public func modelSelected<T>(_ modelType: T.Type) -> ControlEvent<T> {
let source: Observable<T> = itemSelected.flatMap { [weak view = self.base as UICollectionView] indexPath -> Observable<T> in
guard let view = view else {
return Observable.empty()
}
return Observable.just(try view.rx.model(at: indexPath))
}
return ControlEvent(events: source)
}
/// Reactive wrapper for `delegate` message `collectionView(_:didSelectItemAtIndexPath:)`.
///
/// It can be only used when one of the `rx.itemsWith*` methods is used to bind observable sequence,
/// or any other data source conforming to `SectionedViewDataSourceType` protocol.
///
/// ```
/// collectionView.rx.modelDeselected(MyModel.self)
/// .map { ...
/// ```
public func modelDeselected<T>(_ modelType: T.Type) -> ControlEvent<T> {
let source: Observable<T> = itemDeselected.flatMap { [weak view = self.base as UICollectionView] indexPath -> Observable<T> in
guard let view = view else {
return Observable.empty()
}
return Observable.just(try view.rx.model(at: indexPath))
}
return ControlEvent(events: source)
}
/// Synchronous helper method for retrieving a model at indexPath through a reactive data source
public func model<T>(at indexPath: IndexPath) throws -> T {
let dataSource: SectionedViewDataSourceType = castOrFatalError(self.dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx.itemsWith*` methods was used.")
let element = try dataSource.model(at: indexPath)
return try castOrThrow(T.self, element)
}
}
@available(iOS 10.0, tvOS 10.0, *)
extension Reactive where Base: UICollectionView {
/// Reactive wrapper for `prefetchDataSource`.
///
/// For more information take a look at `DelegateProxyType` protocol documentation.
public var prefetchDataSource: DelegateProxy<UICollectionView, UICollectionViewDataSourcePrefetching> {
RxCollectionViewDataSourcePrefetchingProxy.proxy(for: base)
}
/**
Installs prefetch data source as forwarding delegate on `rx.prefetchDataSource`.
Prefetch data source won't be retained.
It enables using normal delegate mechanism with reactive delegate mechanism.
- parameter prefetchDataSource: Prefetch data source object.
- returns: Disposable object that can be used to unbind the data source.
*/
public func setPrefetchDataSource(_ prefetchDataSource: UICollectionViewDataSourcePrefetching)
-> Disposable {
return RxCollectionViewDataSourcePrefetchingProxy.installForwardDelegate(prefetchDataSource, retainDelegate: false, onProxyForObject: self.base)
}
/// Reactive wrapper for `prefetchDataSource` message `collectionView(_:prefetchItemsAt:)`.
public var prefetchItems: ControlEvent<[IndexPath]> {
let source = RxCollectionViewDataSourcePrefetchingProxy.proxy(for: base).prefetchItemsPublishSubject
return ControlEvent(events: source)
}
/// Reactive wrapper for `prefetchDataSource` message `collectionView(_:cancelPrefetchingForItemsAt:)`.
public var cancelPrefetchingForItems: ControlEvent<[IndexPath]> {
let source = prefetchDataSource.methodInvoked(#selector(UICollectionViewDataSourcePrefetching.collectionView(_:cancelPrefetchingForItemsAt:)))
.map { a in
return try castOrThrow(Array<IndexPath>.self, a[1])
}
return ControlEvent(events: source)
}
}
#endif
#if os(tvOS)
extension Reactive where Base: UICollectionView {
/// Reactive wrapper for `delegate` message `collectionView(_:didUpdateFocusInContext:withAnimationCoordinator:)`.
public var didUpdateFocusInContextWithAnimationCoordinator: ControlEvent<(context: UICollectionViewFocusUpdateContext, animationCoordinator: UIFocusAnimationCoordinator)> {
let source = delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didUpdateFocusIn:with:)))
.map { a -> (context: UICollectionViewFocusUpdateContext, animationCoordinator: UIFocusAnimationCoordinator) in
let context = try castOrThrow(UICollectionViewFocusUpdateContext.self, a[1])
let animationCoordinator = try castOrThrow(UIFocusAnimationCoordinator.self, a[2])
return (context: context, animationCoordinator: animationCoordinator)
}
return ControlEvent(events: source)
}
}
#endif

View File

@@ -0,0 +1,86 @@
//
// UIControl+Rx.swift
// RxCocoa
//
// Created by Daniel Tartaglia on 5/23/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import RxSwift
import UIKit
extension Reactive where Base: UIControl {
/// Reactive wrapper for target action pattern.
///
/// - parameter controlEvents: Filter for observed event types.
public func controlEvent(_ controlEvents: UIControl.Event) -> ControlEvent<()> {
let source: Observable<Void> = Observable.create { [weak control = self.base] observer in
MainScheduler.ensureRunningOnMainThread()
guard let control = control else {
observer.on(.completed)
return Disposables.create()
}
let controlTarget = ControlTarget(control: control, controlEvents: controlEvents) { _ in
observer.on(.next(()))
}
return Disposables.create(with: controlTarget.dispose)
}
.take(until: deallocated)
return ControlEvent(events: source)
}
/// Creates a `ControlProperty` that is triggered by target/action pattern value updates.
///
/// - parameter controlEvents: Events that trigger value update sequence elements.
/// - parameter getter: Property value getter.
/// - parameter setter: Property value setter.
public func controlProperty<T>(
editingEvents: UIControl.Event,
getter: @escaping (Base) -> T,
setter: @escaping (Base, T) -> Void
) -> ControlProperty<T> {
let source: Observable<T> = Observable.create { [weak weakControl = base] observer in
guard let control = weakControl else {
observer.on(.completed)
return Disposables.create()
}
observer.on(.next(getter(control)))
let controlTarget = ControlTarget(control: control, controlEvents: editingEvents) { _ in
if let control = weakControl {
observer.on(.next(getter(control)))
}
}
return Disposables.create(with: controlTarget.dispose)
}
.take(until: deallocated)
let bindingObserver = Binder(base, binding: setter)
return ControlProperty<T>(values: source, valueSink: bindingObserver)
}
/// This is a separate method to better communicate to public consumers that
/// an `editingEvent` needs to fire for control property to be updated.
internal func controlPropertyWithDefaultEvents<T>(
editingEvents: UIControl.Event = [.allEditingEvents, .valueChanged],
getter: @escaping (Base) -> T,
setter: @escaping (Base, T) -> Void
) -> ControlProperty<T> {
return controlProperty(
editingEvents: editingEvents,
getter: getter,
setter: setter
)
}
}
#endif

View File

@@ -0,0 +1,43 @@
//
// UIDatePicker+Rx.swift
// RxCocoa
//
// Created by Daniel Tartaglia on 5/31/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import RxSwift
import UIKit
extension Reactive where Base: UIDatePicker {
/// Reactive wrapper for `date` property.
public var date: ControlProperty<Date> {
value
}
/// Reactive wrapper for `date` property.
public var value: ControlProperty<Date> {
return base.rx.controlPropertyWithDefaultEvents(
getter: { datePicker in
datePicker.date
}, setter: { datePicker, value in
datePicker.date = value
}
)
}
/// Reactive wrapper for `countDownDuration` property.
public var countDownDuration: ControlProperty<TimeInterval> {
return base.rx.controlPropertyWithDefaultEvents(
getter: { datePicker in
datePicker.countDownDuration
}, setter: { datePicker, value in
datePicker.countDownDuration = value
}
)
}
}
#endif

View File

@@ -0,0 +1,75 @@
//
// UIGestureRecognizer+Rx.swift
// RxCocoa
//
// Created by Carlos García on 10/6/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
// This should be only used from `MainScheduler`
final class GestureTarget<Recognizer: UIGestureRecognizer>: RxTarget {
typealias Callback = (Recognizer) -> Void
let selector = #selector(ControlTarget.eventHandler(_:))
weak var gestureRecognizer: Recognizer?
var callback: Callback?
init(_ gestureRecognizer: Recognizer, callback: @escaping Callback) {
self.gestureRecognizer = gestureRecognizer
self.callback = callback
super.init()
gestureRecognizer.addTarget(self, action: selector)
let method = self.method(for: selector)
if method == nil {
fatalError("Can't find method")
}
}
@objc func eventHandler(_ sender: UIGestureRecognizer) {
if let callback = self.callback, let gestureRecognizer = self.gestureRecognizer {
callback(gestureRecognizer)
}
}
override func dispose() {
super.dispose()
self.gestureRecognizer?.removeTarget(self, action: self.selector)
self.callback = nil
}
}
extension Reactive where Base: UIGestureRecognizer {
/// Reactive wrapper for gesture recognizer events.
public var event: ControlEvent<Base> {
let source: Observable<Base> = Observable.create { [weak control = self.base] observer in
MainScheduler.ensureRunningOnMainThread()
guard let control = control else {
observer.on(.completed)
return Disposables.create()
}
let observer = GestureTarget(control) { control in
observer.on(.next(control))
}
return observer
}.take(until: deallocated)
return ControlEvent(events: source)
}
}
#endif

View File

@@ -0,0 +1,49 @@
//
// UINavigationController+Rx.swift
// RxCocoa
//
// Created by Diogo on 13/04/17.
// Copyright © 2017 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import RxSwift
import UIKit
extension Reactive where Base: UINavigationController {
public typealias ShowEvent = (viewController: UIViewController, animated: Bool)
/// Reactive wrapper for `delegate`.
///
/// For more information take a look at `DelegateProxyType` protocol documentation.
public var delegate: DelegateProxy<UINavigationController, UINavigationControllerDelegate> {
RxNavigationControllerDelegateProxy.proxy(for: base)
}
/// Reactive wrapper for delegate method `navigationController(:willShow:animated:)`.
public var willShow: ControlEvent<ShowEvent> {
let source: Observable<ShowEvent> = delegate
.methodInvoked(#selector(UINavigationControllerDelegate.navigationController(_:willShow:animated:)))
.map { arg in
let viewController = try castOrThrow(UIViewController.self, arg[1])
let animated = try castOrThrow(Bool.self, arg[2])
return (viewController, animated)
}
return ControlEvent(events: source)
}
/// Reactive wrapper for delegate method `navigationController(:didShow:animated:)`.
public var didShow: ControlEvent<ShowEvent> {
let source: Observable<ShowEvent> = delegate
.methodInvoked(#selector(UINavigationControllerDelegate.navigationController(_:didShow:animated:)))
.map { arg in
let viewController = try castOrThrow(UIViewController.self, arg[1])
let animated = try castOrThrow(Bool.self, arg[2])
return (viewController, animated)
}
return ControlEvent(events: source)
}
}
#endif

View File

@@ -0,0 +1,224 @@
//
// UIPickerView+Rx.swift
// RxCocoa
//
// Created by Segii Shulga on 5/12/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import RxSwift
import UIKit
extension Reactive where Base: UIPickerView {
/// Reactive wrapper for `delegate`.
/// For more information take a look at `DelegateProxyType` protocol documentation.
public var delegate: DelegateProxy<UIPickerView, UIPickerViewDelegate> {
return RxPickerViewDelegateProxy.proxy(for: 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)
}
/**
Reactive wrapper for `dataSource`.
For more information take a look at `DelegateProxyType` protocol documentation.
*/
public var dataSource: DelegateProxy<UIPickerView, UIPickerViewDataSource> {
return RxPickerViewDataSourceProxy.proxy(for: base)
}
/**
Reactive wrapper for `delegate` message `pickerView:didSelectRow:inComponent:`.
*/
public var itemSelected: ControlEvent<(row: Int, component: Int)> {
let source = delegate
.methodInvoked(#selector(UIPickerViewDelegate.pickerView(_:didSelectRow:inComponent:)))
.map {
return (row: try castOrThrow(Int.self, $0[1]), component: try castOrThrow(Int.self, $0[2]))
}
return ControlEvent(events: source)
}
/**
Reactive wrapper for `delegate` message `pickerView:didSelectRow:inComponent:`.
It can be only used when one of the `rx.itemTitles, rx.itemAttributedTitles, items(_ source: O)` methods is used to bind observable sequence,
or any other data source conforming to a `ViewDataSourceType` protocol.
```
pickerView.rx.modelSelected(MyModel.self)
.map { ...
```
- parameter modelType: Type of a Model which bound to the dataSource
*/
public func modelSelected<T>(_ modelType: T.Type) -> ControlEvent<[T]> {
let source = itemSelected.flatMap { [weak view = self.base as UIPickerView] _, component -> Observable<[T]> in
guard let view = view else {
return Observable.empty()
}
let model: [T] = try (0 ..< view.numberOfComponents).map { component in
let row = view.selectedRow(inComponent: component)
return try view.rx.model(at: IndexPath(row: row, section: component))
}
return Observable.just(model)
}
return ControlEvent(events: source)
}
/**
Binds sequences of elements to picker view rows.
- parameter source: Observable sequence of items.
- parameter titleForRow: Transform between sequence elements and row titles.
- returns: Disposable object that can be used to unbind.
Example:
let items = Observable.just([
"First Item",
"Second Item",
"Third Item"
])
items
.bind(to: pickerView.rx.itemTitles) { (row, element) in
return element.title
}
.disposed(by: disposeBag)
*/
public func itemTitles<Sequence: Swift.Sequence, Source: ObservableType>
(_ source: Source)
-> (_ titleForRow: @escaping (Int, Sequence.Element) -> String?)
-> Disposable where Source.Element == Sequence {
return { titleForRow in
let adapter = RxStringPickerViewAdapter<Sequence>(titleForRow: titleForRow)
return self.items(adapter: adapter)(source)
}
}
/**
Binds sequences of elements to picker view rows.
- parameter source: Observable sequence of items.
- parameter attributedTitleForRow: Transform between sequence elements and row attributed titles.
- returns: Disposable object that can be used to unbind.
Example:
let items = Observable.just([
"First Item",
"Second Item",
"Third Item"
])
items
.bind(to: pickerView.rx.itemAttributedTitles) { (row, element) in
return NSAttributedString(string: element.title)
}
.disposed(by: disposeBag)
*/
public func itemAttributedTitles<Sequence: Swift.Sequence, Source: ObservableType>
(_ source: Source)
-> (_ attributedTitleForRow: @escaping (Int, Sequence.Element) -> NSAttributedString?)
-> Disposable where Source.Element == Sequence {
return { attributedTitleForRow in
let adapter = RxAttributedStringPickerViewAdapter<Sequence>(attributedTitleForRow: attributedTitleForRow)
return self.items(adapter: adapter)(source)
}
}
/**
Binds sequences of elements to picker view rows.
- parameter source: Observable sequence of items.
- parameter viewForRow: Transform between sequence elements and row views.
- returns: Disposable object that can be used to unbind.
Example:
let items = Observable.just([
"First Item",
"Second Item",
"Third Item"
])
items
.bind(to: pickerView.rx.items) { (row, element, view) in
guard let myView = view as? MyView else {
let view = MyView()
view.configure(with: element)
return view
}
myView.configure(with: element)
return myView
}
.disposed(by: disposeBag)
*/
public func items<Sequence: Swift.Sequence, Source: ObservableType>
(_ source: Source)
-> (_ viewForRow: @escaping (Int, Sequence.Element, UIView?) -> UIView)
-> Disposable where Source.Element == Sequence {
return { viewForRow in
let adapter = RxPickerViewAdapter<Sequence>(viewForRow: viewForRow)
return self.items(adapter: adapter)(source)
}
}
/**
Binds sequences of elements to picker view rows using a custom reactive adapter used to perform the transformation.
This method will retain the adapter for as long as the subscription isn't disposed (result `Disposable`
being disposed).
In case `source` observable sequence terminates successfully, the adapter will present latest element
until the subscription isn't disposed.
- parameter adapter: Adapter used to transform elements to picker components.
- parameter source: Observable sequence of items.
- returns: Disposable object that can be used to unbind.
*/
public func items<Source: ObservableType,
Adapter: RxPickerViewDataSourceType & UIPickerViewDataSource & UIPickerViewDelegate>(adapter: Adapter)
-> (_ source: Source)
-> Disposable where Source.Element == 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)
}
}
/**
Synchronous helper method for retrieving a model at indexPath through a reactive data source.
*/
public func model<T>(at indexPath: IndexPath) throws -> T {
let dataSource: SectionedViewDataSourceType = castOrFatalError(self.dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx.itemTitles, rx.itemAttributedTitles, items(_ source: O)` methods was used.")
return castOrFatalError(try dataSource.model(at: indexPath))
}
}
#endif

View File

@@ -0,0 +1,28 @@
//
// UIRefreshControl+Rx.swift
// RxCocoa
//
// Created by Yosuke Ishikawa on 1/31/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import UIKit
import RxSwift
extension Reactive where Base: UIRefreshControl {
/// Bindable sink for `beginRefreshing()`, `endRefreshing()` methods.
public var isRefreshing: Binder<Bool> {
return Binder(self.base) { refreshControl, refresh in
if refresh {
refreshControl.beginRefreshing()
} else {
refreshControl.endRefreshing()
}
}
}
}
#endif

View File

@@ -0,0 +1,131 @@
//
// UIScrollView+Rx.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 4/3/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import RxSwift
import UIKit
extension Reactive where Base: UIScrollView {
public typealias EndZoomEvent = (view: UIView?, scale: CGFloat)
public typealias WillEndDraggingEvent = (velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)
/// Reactive wrapper for `delegate`.
///
/// For more information take a look at `DelegateProxyType` protocol documentation.
public var delegate: DelegateProxy<UIScrollView, UIScrollViewDelegate> {
return RxScrollViewDelegateProxy.proxy(for: base)
}
/// Reactive wrapper for `contentOffset`.
public var contentOffset: ControlProperty<CGPoint> {
let proxy = RxScrollViewDelegateProxy.proxy(for: base)
let bindingObserver = Binder(self.base) { scrollView, contentOffset in
scrollView.contentOffset = contentOffset
}
return ControlProperty(values: proxy.contentOffsetBehaviorSubject, valueSink: bindingObserver)
}
/// Reactive wrapper for delegate method `scrollViewDidScroll`
public var didScroll: ControlEvent<Void> {
let source = RxScrollViewDelegateProxy.proxy(for: base).contentOffsetPublishSubject
return ControlEvent(events: source)
}
/// Reactive wrapper for delegate method `scrollViewWillBeginDecelerating`
public var willBeginDecelerating: ControlEvent<Void> {
let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewWillBeginDecelerating(_:))).map { _ in }
return ControlEvent(events: source)
}
/// Reactive wrapper for delegate method `scrollViewDidEndDecelerating`
public var didEndDecelerating: ControlEvent<Void> {
let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidEndDecelerating(_:))).map { _ in }
return ControlEvent(events: source)
}
/// Reactive wrapper for delegate method `scrollViewWillBeginDragging`
public var willBeginDragging: ControlEvent<Void> {
let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewWillBeginDragging(_:))).map { _ in }
return ControlEvent(events: source)
}
/// Reactive wrapper for delegate method `scrollViewWillEndDragging(_:withVelocity:targetContentOffset:)`
public var willEndDragging: ControlEvent<WillEndDraggingEvent> {
let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewWillEndDragging(_:withVelocity:targetContentOffset:)))
.map { value -> WillEndDraggingEvent in
let velocity = try castOrThrow(CGPoint.self, value[1])
let targetContentOffsetValue = try castOrThrow(NSValue.self, value[2])
guard let rawPointer = targetContentOffsetValue.pointerValue else { throw RxCocoaError.unknown }
let typedPointer = rawPointer.bindMemory(to: CGPoint.self, capacity: MemoryLayout<CGPoint>.size)
return (velocity, typedPointer)
}
return ControlEvent(events: source)
}
/// Reactive wrapper for delegate method `scrollViewDidEndDragging(_:willDecelerate:)`
public var didEndDragging: ControlEvent<Bool> {
let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidEndDragging(_:willDecelerate:))).map { value -> Bool in
return try castOrThrow(Bool.self, value[1])
}
return ControlEvent(events: source)
}
/// Reactive wrapper for delegate method `scrollViewDidZoom`
public var didZoom: ControlEvent<Void> {
let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidZoom)).map { _ in }
return ControlEvent(events: source)
}
/// Reactive wrapper for delegate method `scrollViewDidScrollToTop`
public var didScrollToTop: ControlEvent<Void> {
let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidScrollToTop(_:))).map { _ in }
return ControlEvent(events: source)
}
/// Reactive wrapper for delegate method `scrollViewDidEndScrollingAnimation`
public var didEndScrollingAnimation: ControlEvent<Void> {
let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidEndScrollingAnimation(_:))).map { _ in }
return ControlEvent(events: source)
}
/// Reactive wrapper for delegate method `scrollViewWillBeginZooming(_:with:)`
public var willBeginZooming: ControlEvent<UIView?> {
let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewWillBeginZooming(_:with:))).map { value -> UIView? in
return try castOptionalOrThrow(UIView.self, value[1] as AnyObject)
}
return ControlEvent(events: source)
}
/// Reactive wrapper for delegate method `scrollViewDidEndZooming(_:with:atScale:)`
public var didEndZooming: ControlEvent<EndZoomEvent> {
let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidEndZooming(_:with:atScale:))).map { value -> EndZoomEvent in
return (try castOptionalOrThrow(UIView.self, value[1] as AnyObject), try castOrThrow(CGFloat.self, value[2]))
}
return ControlEvent(events: source)
}
/// 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: UIScrollViewDelegate)
-> Disposable {
return RxScrollViewDelegateProxy.installForwardDelegate(delegate, retainDelegate: false, onProxyForObject: self.base)
}
}
#endif

View File

@@ -0,0 +1,136 @@
//
// UISearchBar+Rx.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 3/28/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import RxSwift
import UIKit
extension Reactive where Base: UISearchBar {
/// Reactive wrapper for `delegate`.
///
/// For more information take a look at `DelegateProxyType` protocol documentation.
public var delegate: DelegateProxy<UISearchBar, UISearchBarDelegate> {
RxSearchBarDelegateProxy.proxy(for: base)
}
/// Reactive wrapper for `text` property.
public var text: ControlProperty<String?> {
value
}
/// Reactive wrapper for `text` property.
public var value: ControlProperty<String?> {
let source: Observable<String?> = Observable.deferred { [weak searchBar = self.base as UISearchBar] () -> Observable<String?> in
let text = searchBar?.text
let textDidChange = (searchBar?.rx.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBar(_:textDidChange:))) ?? Observable.empty())
let didEndEditing = (searchBar?.rx.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBarTextDidEndEditing(_:))) ?? Observable.empty())
return Observable.merge(textDidChange, didEndEditing)
.map { _ in searchBar?.text ?? "" }
.startWith(text)
}
let bindingObserver = Binder(self.base) { (searchBar, text: String?) in
searchBar.text = text
}
return ControlProperty(values: source, valueSink: bindingObserver)
}
/// Reactive wrapper for `selectedScopeButtonIndex` property.
public var selectedScopeButtonIndex: ControlProperty<Int> {
let source: Observable<Int> = Observable.deferred { [weak source = self.base as UISearchBar] () -> Observable<Int> in
let index = source?.selectedScopeButtonIndex ?? 0
return (source?.rx.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBar(_:selectedScopeButtonIndexDidChange:))) ?? Observable.empty())
.map { a in
return try castOrThrow(Int.self, a[1])
}
.startWith(index)
}
let bindingObserver = Binder(self.base) { (searchBar, index: Int) in
searchBar.selectedScopeButtonIndex = index
}
return ControlProperty(values: source, valueSink: bindingObserver)
}
#if os(iOS)
/// Reactive wrapper for delegate method `searchBarCancelButtonClicked`.
public var cancelButtonClicked: ControlEvent<Void> {
let source: Observable<Void> = self.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBarCancelButtonClicked(_:)))
.map { _ in
return ()
}
return ControlEvent(events: source)
}
/// Reactive wrapper for delegate method `searchBarBookmarkButtonClicked`.
public var bookmarkButtonClicked: ControlEvent<Void> {
let source: Observable<Void> = self.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBarBookmarkButtonClicked(_:)))
.map { _ in
return ()
}
return ControlEvent(events: source)
}
/// Reactive wrapper for delegate method `searchBarResultsListButtonClicked`.
public var resultsListButtonClicked: ControlEvent<Void> {
let source: Observable<Void> = self.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBarResultsListButtonClicked(_:)))
.map { _ in
return ()
}
return ControlEvent(events: source)
}
#endif
/// Reactive wrapper for delegate method `searchBarSearchButtonClicked`.
public var searchButtonClicked: ControlEvent<Void> {
let source: Observable<Void> = self.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBarSearchButtonClicked(_:)))
.map { _ in
return ()
}
return ControlEvent(events: source)
}
/// Reactive wrapper for delegate method `searchBarTextDidBeginEditing`.
public var textDidBeginEditing: ControlEvent<Void> {
let source: Observable<Void> = self.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBarTextDidBeginEditing(_:)))
.map { _ in
return ()
}
return ControlEvent(events: source)
}
/// Reactive wrapper for delegate method `searchBarTextDidEndEditing`.
public var textDidEndEditing: ControlEvent<Void> {
let source: Observable<Void> = self.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBarTextDidEndEditing(_:)))
.map { _ in
return ()
}
return ControlEvent(events: source)
}
/// 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: UISearchBarDelegate)
-> Disposable {
RxSearchBarDelegateProxy.installForwardDelegate(delegate, retainDelegate: false, onProxyForObject: self.base)
}
}
#endif

View File

@@ -0,0 +1,58 @@
//
// UISearchController+Rx.swift
// RxCocoa
//
// Created by Segii Shulga on 3/17/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import RxSwift
import UIKit
extension Reactive where Base: UISearchController {
/// Reactive wrapper for `delegate`.
/// For more information take a look at `DelegateProxyType` protocol documentation.
public var delegate: DelegateProxy<UISearchController, UISearchControllerDelegate> {
return RxSearchControllerDelegateProxy.proxy(for: base)
}
/// Reactive wrapper for `delegate` message.
public var didDismiss: Observable<Void> {
return delegate
.methodInvoked( #selector(UISearchControllerDelegate.didDismissSearchController(_:)))
.map { _ in }
}
/// Reactive wrapper for `delegate` message.
public var didPresent: Observable<Void> {
return delegate
.methodInvoked(#selector(UISearchControllerDelegate.didPresentSearchController(_:)))
.map { _ in }
}
/// Reactive wrapper for `delegate` message.
public var present: Observable<Void> {
return delegate
.methodInvoked( #selector(UISearchControllerDelegate.presentSearchController(_:)))
.map { _ in }
}
/// Reactive wrapper for `delegate` message.
public var willDismiss: Observable<Void> {
return delegate
.methodInvoked(#selector(UISearchControllerDelegate.willDismissSearchController(_:)))
.map { _ in }
}
/// Reactive wrapper for `delegate` message.
public var willPresent: Observable<Void> {
return delegate
.methodInvoked( #selector(UISearchControllerDelegate.willPresentSearchController(_:)))
.map { _ in }
}
}
#endif

View File

@@ -0,0 +1,54 @@
//
// UISegmentedControl+Rx.swift
// RxCocoa
//
// Created by Carlos García on 8/7/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
extension Reactive where Base: UISegmentedControl {
/// Reactive wrapper for `selectedSegmentIndex` property.
public var selectedSegmentIndex: ControlProperty<Int> {
value
}
/// Reactive wrapper for `selectedSegmentIndex` property.
public var value: ControlProperty<Int> {
return base.rx.controlPropertyWithDefaultEvents(
getter: { segmentedControl in
segmentedControl.selectedSegmentIndex
}, setter: { segmentedControl, value in
segmentedControl.selectedSegmentIndex = value
}
)
}
/// Reactive wrapper for `setEnabled(_:forSegmentAt:)`
public func enabledForSegment(at index: Int) -> Binder<Bool> {
return Binder(self.base) { segmentedControl, segmentEnabled -> Void in
segmentedControl.setEnabled(segmentEnabled, forSegmentAt: index)
}
}
/// Reactive wrapper for `setTitle(_:forSegmentAt:)`
public func titleForSegment(at index: Int) -> Binder<String?> {
return Binder(self.base) { segmentedControl, title -> Void in
segmentedControl.setTitle(title, forSegmentAt: index)
}
}
/// Reactive wrapper for `setImage(_:forSegmentAt:)`
public func imageForSegment(at index: Int) -> Binder<UIImage?> {
return Binder(self.base) { segmentedControl, image -> Void in
segmentedControl.setImage(image, forSegmentAt: index)
}
}
}
#endif

View File

@@ -0,0 +1,29 @@
//
// UISlider+Rx.swift
// RxCocoa
//
// Created by Alexander van der Werff on 28/05/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import RxSwift
import UIKit
extension Reactive where Base: UISlider {
/// Reactive wrapper for `value` property.
public var value: ControlProperty<Float> {
return base.rx.controlPropertyWithDefaultEvents(
getter: { slider in
slider.value
}, setter: { slider, value in
slider.value = value
}
)
}
}
#endif

View File

@@ -0,0 +1,29 @@
//
// UIStepper+Rx.swift
// RxCocoa
//
// Created by Yuta ToKoRo on 9/1/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import UIKit
import RxSwift
extension Reactive where Base: UIStepper {
/// Reactive wrapper for `value` property.
public var value: ControlProperty<Double> {
return base.rx.controlPropertyWithDefaultEvents(
getter: { stepper in
stepper.value
}, setter: { stepper, value in
stepper.value = value
}
)
}
}
#endif

View File

@@ -0,0 +1,38 @@
//
// UISwitch+Rx.swift
// RxCocoa
//
// Created by Carlos García on 8/7/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import UIKit
import RxSwift
extension Reactive where Base: UISwitch {
/// Reactive wrapper for `isOn` property.
public var isOn: ControlProperty<Bool> {
value
}
/// Reactive wrapper for `isOn` property.
///
/// Versions prior to iOS 10.2 were leaking `UISwitch`'s, so on those versions
/// underlying observable sequence won't complete when nothing holds a strong reference
/// to `UISwitch`.
public var value: ControlProperty<Bool> {
return base.rx.controlPropertyWithDefaultEvents(
getter: { uiSwitch in
uiSwitch.isOn
}, setter: { uiSwitch, value in
uiSwitch.isOn = value
}
)
}
}
#endif

Some files were not shown because too many files have changed in this diff Show More