Skip to content

Instantly share code, notes, and snippets.

@mishagray
Created January 15, 2016 03:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mishagray/08b24f8a8bea0a711af9 to your computer and use it in GitHub Desktop.
Save mishagray/08b24f8a8bea0a711af9 to your computer and use it in GitHub Desktop.
//
// RAC+Extensions.swift
// CoverPages
//
// Created by Morten Heiberg on 12/1/15.
// Copyright (c) 2015 Squarespace. All rights reserved.
//
import Foundation
import ReactiveCocoa
import FutureKit
/////////////////////////////////////////////////////////
// MARK: addErrorHandler Support
/////////////////////////////////////////////////////////
/**
extending an ErrorType with SignalProducer_AddErrorHandler_Checking, will cause start() methods to be depricated on StartProducer, for that ErrorType.
This will encourage developers to insert an addErrorHandler() or handleErrors() method before calling start for the given ErrorType.
Don't ever extend NoError with SignalProducer_AddErrorHandler_Checking, or things will go a bit wonky
*/
public protocol SignalProducer_AddErrorHandler_Checking : ErrorType {}
extension NSError : SignalProducer_AddErrorHandler_Checking {}
// extending the SignalProducer, NOT the protocol SignalProducerType!
// this will cause the SignalProducerType 'start' calls to basically become depricated.
extension SignalProducer where Error : SignalProducer_AddErrorHandler_Checking {
// So there is always a worry that SignalProducerType.start() implmentation changes in the future
// This is really for lazy developers who don't add an error strategy. If you add one, than then this method is never called.
public func _start(observer: Signal<Value, Error>.Observer = Signal<Value, Error>.Observer()) -> Disposable {
var disposable: Disposable!
startWithSignal { signal, innerDisposable in
signal.observe(observer)
disposable = innerDisposable
}
return disposable
}
@available(*, deprecated=0.0, message="hey - did you add an addErrorHandler() or handleErrors() method before calling start()")
public func start(observerAction: Signal<Value, Error>.Observer.Action) -> Disposable {
return _start(Observer(observerAction))
}
@available(*, deprecated=0.0, message="hey - did you add an addErrorHandler() or handleErrors() method before calling startWithNext()??")
public func startWithNext(next: Value -> ()) -> Disposable {
return _start(Observer(next: next))
}
@available(*, deprecated=0.0, message="hey - did you add an addErrorHandler() or handleErrors() method before calling startWithCompleted()??")
public func startWithCompleted(completed: () -> ()) -> Disposable {
return _start(Observer(completed: completed))
}
@available(*, deprecated=0.0, message="hey - did you add an addErrorHandler() or handleErrors() method before calling startWithInterrupted()??")
public func startWithInterrupted(interrupted: () -> ()) -> Disposable {
return _start(Observer(interrupted: interrupted))
}
}
/**
This is SignalProducerType that has an error handler, and is returned from addErrorHandler().
It really is just a Type Proxy that forwards the start request to an actual SignalProducer, but avoids the compiler deprication warning when calling start()
*/
public struct SignalProducerWithErrorHandler<T, E: ErrorType> : SignalProducerType {
public var producer: SignalProducer<T, E>
init(producer p:SignalProducer<T, E>) {
self.producer = p
}
/// Creates a Signal from the producer, passes it into the given closure,
/// then starts sending events on the Signal when the closure has returned.
public func startWithSignal(@noescape setUp: (Signal<T, E>, Disposable) -> ()) {
return producer.startWithSignal(setUp)
}
}
/**
A simple enumeration that defines a set of ErrorHandling Strategies.
*/
public enum ErrorHandlingStrategy<E : ErrorType> {
/**
Errors are ignored. No logging or nothing! Use with care.
*/
case Ignore
/**
Errors are logged, but mostly still ignored.
*/
case Log // logs the error to the console, log
/**
The user is alerted of the Error.
*/
// case Alert // shows an alertview box
/**
assertFailure() is called. These calls are removed for Release builds.
*/
case Assert
/**
fatalError() is called. This is NOT ignored in Release builds.
*/
case HaltApp // always halts application
/**
Handle the errors with a block.
*/
case SendToErrorSink(E -> ()) // Custom handling of the error via some block
/**
performs error handling, depending on the selected strategy.
*/
func handleError(e : E,functionName: StaticString = __FUNCTION__, fileName: StaticString = __FILE__, lineNumber: UInt = __LINE__) {
switch self {
case Ignore:
break
case Log:
let message = "Unexpected error seen \(e) from \(functionName) in file \(fileName),\(lineNumber)"
log(message)
// case Alert:
// log("INSERT ALERT VIEW HANDLING FOR THIS ERROR \(e)")
case Assert:
assertionFailure("Unexpected error seen \(e) from \(functionName)", file:fileName, line:lineNumber)
case HaltApp:
fatalError("Unexpected error seen \(e) from \(functionName)", file:fileName, line:lineNumber)
case let SendToErrorSink(sink):
sink(e)
}
}
}
public extension SignalProducerType {
/**
Adds an Error Handler to the SignalProducer.
By default, ErrorTypes that have been extended to `SignalProducer_AddErrorHandler_Checking` will generate a depcrated warning, if you call start() without adding an ErrorHandler.
Errors are still forwarded to the signal. You may want to consider 'handleErrors()' if you wish this to be the primary error handling strategy.
*/
func addErrorHandler(strategy: ErrorHandlingStrategy<Error> = .Assert,functionName: StaticString = __FUNCTION__, fileName: StaticString = __FILE__, lineNumber: UInt = __LINE__) -> SignalProducerWithErrorHandler<Value,Error> {
let safeProducer = self.mapError { error -> Error in
strategy.handleError(error,functionName:functionName,fileName:fileName,lineNumber:lineNumber)
return error
}
return SignalProducerWithErrorHandler(producer:safeProducer)
}
/**
Adds an Error Handler to the SignalProducer. The SignalProducer is converted to a SignalProducer<T,NoError>.
Errors are converted into Interrupts.
By default, ErrorTypes that have been extended to `SignalProducer_AddErrorHandler_Checking` will generate a depcrated warning, if you call start() without adding an ErrorHandler.
*/
func handleErrors(strategy: ErrorHandlingStrategy<Error> = .Assert,functionName: StaticString = __FUNCTION__, fileName: StaticString = __FILE__, lineNumber: UInt = __LINE__) -> SignalProducer<Value,NoError> {
return self.addErrorHandler(strategy,functionName:functionName,fileName:fileName,lineNumber:lineNumber).mapErrorsToInterrupts()
}
/**
Converts any Errors into Interrupts. Does not provide any error handling. Consider using `handleErrors(strategy:)` instead to consume errors
*/
func mapErrorsToInterrupts() -> SignalProducer<Value,NoError> {
return lift { $0.mapErrorsToInterrupts() }
}
/**
Converts any Errors into Completions. Does not provide any error handling. Consider using `handleErrors(strategy:)` instead to consume errors
*/
func mapErrorsToCompletions() -> SignalProducer<Value,NoError> {
return lift { $0.mapErrorsToCompletions() }
}
}
extension SignalType {
func addErrorHandler(strategy: ErrorHandlingStrategy<Error> = .Assert) -> Signal<Value,Error> {
return self.mapError { error in
strategy.handleError(error)
return error
}
}
/**
convert any .Errors to .Interrupted
*/
private func mapErrorsToInterrupts() -> Signal<Value,NoError> {
return Signal<Value, NoError> { observer in
let signalDisposable = self.observe { event in
switch event {
case let .Next(t):
observer.sendNext(t)
case .Completed:
observer.sendCompleted()
case .Failed, .Interrupted:
observer.sendInterrupted()
}
}
return signalDisposable
}
}
/**
convert any .Errors to .Interrupted
*/
private func mapErrorsToCompletions() -> Signal<Value,NoError> {
return Signal<Value, NoError> { observer in
let signalDisposable = self.observe { event in
switch event {
case let .Next(t):
observer.sendNext(t)
case .Completed:
observer.sendCompleted()
case .Interrupted:
observer.sendInterrupted()
case .Failed:
observer.sendCompleted()
}
}
return signalDisposable
}
}
}
/// I added these for backward compatiblity
public func sendNext<Value,Error>(observer:Observer<Value,Error>,_ value: Value) {
observer.sendNext(value)
}
public func sendError<Value,Error>(observer:Observer<Value,Error>,_ error: Error) {
observer.sendFailed(error)
}
public func sendCompleted<Value,Error>(observer:Observer<Value,Error>) {
observer.sendCompleted()
}
public func sendInterrupted<Value,Error>(observer:Observer<Value,Error>) {
observer.sendInterrupted()
}
extension SignalProducerType {
/// Injects side effects to be performed upon the specified signal events.
@warn_unused_result(message="Did you forget to call `start` on the producer?")
public func on(started started: (() -> ())? = nil, event: (ReactiveCocoa.Event<Self.Value, Self.Error> -> ())? = nil, error: (Self.Error -> ())?, completed: (() -> ())? = nil, interrupted: (() -> ())? = nil, terminated: (() -> ())? = nil, disposed: (() -> ())? = nil, next: (Self.Value -> ())? = nil) -> ReactiveCocoa.SignalProducer<Self.Value, Self.Error> {
return self.on(started: started, event: event, failed: error, completed: completed, interrupted: interrupted, terminated: terminated, disposed: disposed, next: next)
}
public func startWithError(failed: Error -> ()) -> Disposable {
return startWithFailed(failed)
}
}
/////////////////////////////////////////////////////////
// MARK: RACSignal to Signal<T,E>
/////////////////////////////////////////////////////////
extension RACSignal {
/** Converts the RAC Signal into a Signal<T,E>.
similar to toSignalProducer().start(), but you don't get a disposable!
*/
public func toSignal<T,E : ErrorType>(transform : AnyObject? -> T, mapError : NSError? -> E) -> Signal<T, E> {
return Signal<T, E> { observer in
let next = { (obj: AnyObject?) -> () in
observer.sendNext(transform(obj))
}
let error = { (nsError: NSError?) -> () in
observer.sendFailed(mapError(nsError))
}
let completed = {
observer.sendCompleted()
}
return self.subscribeNext(next, error: error, completed: completed)
}
}
/** Converts the RAC Signal into a Signal<T,NoError>.
equivilant to toSignalProducer().start(), but you don't get a disposable!
*/
public func toSignal<T>(transform : AnyObject? -> T) -> Signal<T, NoError> {
return Signal<T, NoError> { observer in
let next = { (obj: AnyObject?) -> () in
observer.sendNext(transform(obj))
}
let error = { (nsError: NSError?) -> () in
observer.sendInterrupted()
}
let completed = {
observer.sendCompleted()
}
return self.subscribeNext(next, error: error, completed: completed)
}
}
public func toSignal() -> Signal<AnyObject?, NoError> {
return toSignal { $0 }
}
}
/////////////////////////////////////////////////////////
// MARK: TakeUntilDealloc()
/////////////////////////////////////////////////////////
/// Erase all values on a signal and convert them to empty next events. Errors are discarded.
/// This makes the Signal usable in takeUntil.
@available(*, deprecated=1.0, message="replaced with voidify(). Also see takeUntilDealloc()", renamed="voidify")
public func erase<T, E>(signal: Signal<T, E>) -> Signal<(), NoError> {
return signal.voidify()
}
@available(*, deprecated=1.0, message="replaced with voidify(). Also see takeUntilDealloc()", renamed="voidify")
public func erase<T, E>(signalProducer: SignalProducer<T, E>) -> SignalProducer<(), NoError> {
return signalProducer.lift(erase)
}
/// Erase all values on a RACSignal and convert them to empty next events on a Signal. Errors are discarded.
/// This makes the RACSignal usable in takeUntil.
@available(*, deprecated=1.0, message="replaced with voidify(). Also see takeUntilDealloc()", renamed="voidify")
public func erase(signal: RACSignal) -> Signal<(), NoError> {
return signal.voidify()
}
extension NSObject {
/**
just like RAC2 rac_willDeallocSignal, but cleaned up for RAC 4
*/
func rac_willDeInitSignalProducer() -> SignalProducer<(), NoError> {
return self.rac_willDeallocSignal().toSignalProducer().voidify()
}
/**
just like RAC2 rac_willDeallocSignal, but cleaned up for RAC 4
*/
func rac_willDeInitSignal() -> Signal<(), NoError> {
return self.rac_willDeallocSignal().voidify()
}
}
extension SignalType {
/**
converts data to () and maps errors to Interrupts.
This makes it friendly to use with takeUntil()
*/
@warn_unused_result(message="Did you forget to call `observe` on the signal?")
public final func voidify(strategy: ErrorHandlingStrategy<Error> = .Assert) -> Signal<(), NoError> {
return Signal<(), NoError> { observer in
let signalDisposable = self.observe { event in
switch event {
case .Next(_):
observer.sendNext()
case .Completed:
observer.sendCompleted()
case .Interrupted:
observer.sendInterrupted()
case let .Failed(e):
strategy.handleError(e)
observer.sendInterrupted()
}
}
return signalDisposable
}
}
@warn_unused_result(message="Did you forget to call `observe` on the signal?")
public final func takeUntilDealloc(object : NSObject) -> Signal<Value, Error> {
return takeUntil(object.rac_willDeallocSignal().voidify() )
}
public func observeNextUntilDealloc<O:NSObject>(object:O,next: (O,Value) -> ()) -> Disposable? {
return takeUntilDealloc(object).observeNext { [weak object] in
if let o = object {
next(o,$0)
}
}
}
}
extension SignalProducerType {
@warn_unused_result(message="Did you forget to call `start` on the producer?")
public final func voidify() -> SignalProducer<(), NoError> {
return lift { $0.voidify() }
}
@warn_unused_result(message="Did you forget to call `start` on the producer?")
public final func takeUntilDealloc(object : NSObject) -> SignalProducer<Value, Error> {
return lift { $0.takeUntilDealloc(object) }
}
@warn_unused_result(message="Did you forget to call `start` on the producer?")
public final func takeWhileTrue(test : (Value) -> Bool) -> SignalProducer<Value, Error> {
return SignalProducer<Value, Error> { (observer,disposable) in
let innerDisposable = self.start { event in
switch event {
case let .Next(value):
if test(value) {
observer.sendNext(value)
}
else {
observer.sendCompleted()
disposable.dispose()
}
default:
observer.action(event)
}
}
disposable += innerDisposable
}
}
/**
start a signal, and keep it running until the passed object deallocates.
Will store a weak copy of the object and send to the supplied closure if the object still exists
*/
public final func startWithNextUntilDealloc<O : NSObject>(object : O,next: (strongObject:O,next:Value)-> ()) -> Disposable {
return takeUntilDealloc(object)
.start(Observer<Value,Error>(next:{ [weak object] in
if let strongObject = object {
next(strongObject: strongObject,next: $0)
}
}))
}
}
extension RACSignal {
// converts data to () and strips errors (see erase() above)
@warn_unused_result(message="Did you forget to call `observe` on the signal?")
public final func voidify() -> Signal<(), NoError> {
return toSignal { _ in () }
}
@warn_unused_result(message="Did you forget to call `observe` on the signal?")
public func takeUntilDealloc(object : NSObject) -> RACSignal {
return takeUntil(object.rac_willDeallocSignal())
}
}
/////////////////////////////////////////////////////////
// MARK: takeUntilReuse()
/////////////////////////////////////////////////////////
/**
This defines a common protocol for UITableViewCell and UICollectionReusableView, that both have a rac_prepareForReuseSignal
*/
public protocol HasPrepareForReuseSignal : AnyObject {
var rac_prepareForReuseSignal : RACSignal! { get }
}
extension UITableViewCell : HasPrepareForReuseSignal {}
extension UICollectionReusableView : HasPrepareForReuseSignal {}
extension HasPrepareForReuseSignal {
func rac_prepareForReuseProducer() -> SignalProducer<(), NoError> {
return self.rac_prepareForReuseSignal.toSignalProducer().voidify()
}
func rac_prepareForReuseSignalType() -> Signal<(), NoError> {
return self.rac_prepareForReuseSignal.voidify()
}
}
extension SignalType {
/**
will close the signal if the Object deallocs or prepareForReuse is called
*/
@warn_unused_result(message="Did you forget to call `observe` on the signal?")
public final func takeUntilReuse<O : NSObject where O : HasPrepareForReuseSignal>(cell : O) -> Signal<Value, Error> {
return takeUntil(cell.rac_prepareForReuseSignal.voidify())
.takeUntilDealloc(cell)
}
}
extension SignalProducerType {
/**
will close the signal if the Object deallocs or prepareForReuse is called
*/
@warn_unused_result(message="Did you forget to call `start` on the producer?")
public final func takeUntilReuse<O : NSObject where O : HasPrepareForReuseSignal>(cell : O) -> SignalProducer<Value, Error> {
return lift { $0.takeUntilReuse(cell) }
}
}
extension RACSignal {
@warn_unused_result(message="Did you forget to call `observe` on the signal?")
public func takeUntilReuse<O : NSObject where O : HasPrepareForReuseSignal>(object : O) -> RACSignal {
return takeUntil(object.rac_prepareForReuseSignal).takeUntilDealloc(object)
}
}
/////////////////////////////////////////////////////////
// MARK: skipValuesChangedRecently() and NonRepeatingMutableProperty
/////////////////////////////////////////////////////////
extension SignalType {
@warn_unused_result(message="Did you forget to call `observe` on the signal?")
public final func skipValuesChangedRecently() -> Signal<Value, Error> {
// create a lastDisposale and a serial dispatch queue
var lastDisposable: Disposable? = nil
let q = QueueScheduler.mainQueueScheduler
return Signal { observer in
return self.observe{ event in
switch event {
case .Next(_):
lastDisposable?.dispose()
lastDisposable = q.schedule({ () -> () in
observer.action(event)
})
default:
observer.action(event)
}
}
}
}
}
extension SignalProducerType {
@warn_unused_result(message="Did you forget to call `start` on the producer?")
public final func skipValuesChangedRecently() -> SignalProducer<Value, Error> {
return lift { $0.skipValuesChangedRecently() }
}
}
/// A mutable property of type T that allows observation of its changes.
///
/// Instances of this class are thread-safe.
public final class NonRepeatingMutableProperty<T : Equatable>: MutablePropertyType {
public typealias Value = T
let mutableProperty : MutableProperty<T>
/// The current value of the property.
///
/// Setting this to a new value will notify all observers of any Signals
/// created from the `values` producer.
public var value: T {
get {
return mutableProperty.value
}
set {
mutableProperty.value = newValue
}
}
/// A producer for Signals that will send the property's current value,
/// followed by all changes over time, then complete when the property has
/// deinitialized.
public var producer: SignalProducer<T, NoError> {
return (mutableProperty.producer.skipRepeats())
}
/// Initializes the property with the given value to start.
public init(_ initialValue: T) {
mutableProperty = MutableProperty<T>(initialValue)
}
}
extension UIView {
// let's you 'observe' when a UIView frame has changed. (autolayout change, rotation change).
var frameChangeProducer: SignalProducer<CGRect, NoError> {
return DynamicProperty(object: self, keyPath: "frame").producer.map { ($0 as! NSValue).CGRectValue() }.skipRepeats()
}
}
extension PropertyType {
func map<U>(f: Value -> U) -> AnyProperty<U> {
let initialValue = f(self.value)
let producer = self.producer.map(f)
return AnyProperty<U>(initialValue: initialValue,producer: producer)
}
}
func monitorForChanges(constraints : [NSLayoutConstraint]) -> SignalProducer<Any, NoError> {
let constraintProperties = constraints.map {
DynamicProperty(object: $0, keyPath: "constant").map {
($0 as! NSNumber).doubleValue
}
}
return monitorForChanges(constraintProperties)
}
// this takes an array of PropertyTypes that are equatable
func monitorForChanges<P: ReactiveCocoa.PropertyType, T : Equatable where P.Value == T>(properties : [P]) -> SignalProducer<Any, NoError> {
return monitorForChanges(properties.map { $0.producer })
}
func monitorForChanges<T, E>(producers : [SignalProducer<T,E>],isRepeat: (T, T) -> Bool) -> SignalProducer<Any, E> {
// first we convert these producers into a simple () producer that transmits when the original producer changes values
let onValueChangeProducers = producers.map { $0.skipRepeats(isRepeat).map { $0 as Any } }
// now let's merge all these
let (signal,sink) = SignalProducer<SignalProducer<Any, E>, E>.buffer(5)
for p in onValueChangeProducers {
sendNext(sink,p)
}
sendCompleted(sink)
let mergedOnValueChangedProducer = signal.flatten(FlattenStrategy.Merge)
// now we are gonna throw away values that happen simultaneously
// return mergedOnValueChangedProducer.skipValuesChangedRecently()
return mergedOnValueChangedProducer
}
func monitorForChanges<T : Equatable, E>(producers : [SignalProducer<T,E>]) -> SignalProducer<Any, E> {
return monitorForChanges(producers) { $0 == $1 }
}
// -------------------
// MARK: OLD RAC
// -------------------
// MARK: RACBox
/// For sending Swift types through a RAC signal.
class RACBox<T>: NSObject {
let unbox: () -> T
init(_ obj: T) {
unbox = { obj }
}
}
// MARK: RACObserve
public func RACObserve(target: NSObject!, keyPath: String) -> RACSignal {
return target.rac_valuesForKeyPath(keyPath, observer: target)
}
// MARK: Replacement for the RAC macro
// Example use:
// searchTextField.rac_textSignal() ~> RAC(viewModel, "searchText")
public struct RAC {
var target : NSObject!
var keyPath : String!
var nilValue : AnyObject!
public init(_ target: NSObject!, _ keyPath: String, nilValue: AnyObject? = nil) {
self.target = target
self.keyPath = keyPath
self.nilValue = nilValue
}
public func assignSignal(signal : RACSignal) {
signal.setKeyPath(self.keyPath, onObject: self.target, nilValue: self.nilValue)
}
}
infix operator ~> {}
public func ~> (signal: RACSignal, rac: RAC) {
rac.assignSignal(signal)
}
// MARK: Type-safe signal manipulation
public extension RACSignal {
public func subscribeNextAs<T>(nextClosure:(T) -> ()) -> () {
self.safetyFilter({ $0 is T }).subscribeNext { next in
nextClosure(next as! T)
}
}
public func mapAs<T: AnyObject, U: AnyObject>(mapClosure:(T) -> U) -> RACSignal {
return self.safetyFilter({ $0 is T }).map { next in
return mapClosure(next as! T)
}
}
public func filterAs<T: AnyObject>(filterClosure:(T) -> Bool) -> RACSignal {
return self.safetyFilter({ $0 is T }).filter { next in
return filterClosure(next as! T)
}
}
public func doNextAs<T: AnyObject>(nextClosure:(T) -> ()) -> RACSignal {
return self.safetyFilter({ $0 is T }).doNext { next in
nextClosure(next as! T)
}
}
public func flattenMapAs<T: AnyObject>(mapClosure:((T) -> RACStream!)!) -> RACSignal {
return self.safetyFilter({ $0 is T }).flattenMap { next in
return mapClosure(next as! T)
}
}
// This could be replaced by a vanilla call to filter, except we want a warning to catch the cases
// where we subscribe to a 3rd party signal not realizing that it might send objects of other types
// or if we change a signal ourselves which has subscriber expecting objects of a certain type.
private func safetyFilter(expected: (AnyObject!) -> (Bool)) -> RACSignal {
return self.filter { next in
if !expected(next) {
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// If you get this warning you should add an explicit filter checking other types or nil.
// Don't rely on the type-safe convenience methods for filtering heterogenous streams.
log("WARNING: Unexpected value found on signal has been ignored.")
return false
}
return true
}
}
}
public class RACSignalEx {
public class func combineLatestAs<T, U, R: AnyObject>(signals:[RACSignal], reduce:(T,U) -> R) -> RACSignal {
return RACSignal.combineLatest(signals).mapAs {
(tuple: RACTuple) -> R in
return reduce(tuple.first as! T, tuple.second as! U)
}
}
}
// MARK: Two-way bindings
public struct RACChannelTo {
var target : NSObject!
var keyPath : String!
var nilValue : AnyObject!
init(_ target: NSObject!, _ keyPath: String, nilValue: AnyObject? = nil) {
self.target = target
self.keyPath = keyPath
self.nilValue = nilValue
}
func channelTerminal() -> RACChannelTerminal {
let kvoChannel: RACKVOChannel = RACKVOChannel(target: self.target, keyPath: self.keyPath, nilValue: self.nilValue)
return kvoChannel.followingTerminal
}
}
infix operator <~> {}
infix operator ❤️ {}
public func <~> (channel: RACChannelTo, otherTerminal: RACChannelTerminal) {
let thisTerminal = channel.channelTerminal()
otherTerminal.subscribe(thisTerminal)
thisTerminal.skip(1).subscribe(otherTerminal)
}
public func <~> (otherTerminal: RACChannelTerminal, channel: RACChannelTo) {
let thisTerminal = channel.channelTerminal()
thisTerminal.subscribe(otherTerminal)
otherTerminal.skip(1).subscribe(thisTerminal)
}
public func ❤️ (channel: RACChannelTo, otherTerminal: RACChannelTerminal) {
let thisTerminal = channel.channelTerminal()
otherTerminal.subscribe(thisTerminal)
thisTerminal.skip(1).subscribe(otherTerminal)
}
public func ❤️ (otherTerminal: RACChannelTerminal, channel: RACChannelTo) {
let thisTerminal = channel.channelTerminal()
thisTerminal.subscribe(otherTerminal)
otherTerminal.skip(1).subscribe(thisTerminal)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment