import Cocoa import RxSwift import RxCocoa import XCPlayground XCPlaygroundPage.currentPage.needsIndefiniteExecution = true example("without shareReplay duplicate call problem") { let source = NSTextField() let status = NSTextField() let URL = NSURL(string: "https://github.com/")! let request = NSURLRequest(URL: URL) let observable = NSURLSession.sharedSession().rx_response(request).debug("http") let sourceObservable = observable.map { (maybeData, response) in return String(data: maybeData, encoding: NSUTF8StringEncoding)! }.observeOn(MainScheduler.instance) let statusObservable = observable.map { (maybeData, response) in return response.statusCode.description }.observeOn(MainScheduler.instance) sourceObservable.subscribe(source.rx_text) statusObservable.subscribe(status.rx_text) }
--- without shareReplay duplicate call problem example --- 2016-05-01 04:17:17.225: http -> subscribed 2016-05-01 04:17:17.229: http -> subscribed curl -X GET "https://github.com/" -i -v Success (1098ms): Status 200 2016-05-01 04:17:18.326: http -> Event Next((<OS_dispatch_d...; mode=block"; } })) 2016-05-01 04:17:18.339: http -> Event Completed 2016-05-01 04:17:18.339: http -> disposed curl -X GET "https://github.com/" -i -v Success (1326ms): Status 200 2016-05-01 04:17:18.556: http -> Event Next((<OS_dispatch_d...; mode=block"; } })) 2016-05-01 04:17:18.557: http -> Event Completed 2016-05-01 04:17:18.557: http -> disposed
let observable = NSURLSession.sharedSession().rx_response(request).debug("http").shareReplayLatestWhileConnected()
--- with shareReplay no duplicate call problem example --- 2016-05-01 04:18:27.845: http -> subscribed curl -X GET "https://github.com/" -i -v Success (960ms): Status 200 2016-05-01 04:18:28.807: http -> Event Next((<OS_dispatch_d...; mode=block"; } })) 2016-05-01 04:18:28.820: http -> Event Completed 2016-05-01 04:18:28.821: http -> disposed
fatalError "fatal error: Executing on backgound thread. Please use `MainScheduler.instance.schedule` to schedule work on main thread."
example("from main thread") { print("init thread: \(NSThread.currentThread())") let source = NSTextField() let status = NSTextField() let URL = NSURL(string: "https://github.com/")! let request = NSURLRequest(URL: URL) let observable = NSURLSession.sharedSession().rx_response(request).shareReplayLatestWhileConnected() let sourceObservable = observable.map { (maybeData, response) in return String(data: maybeData, encoding: NSUTF8StringEncoding)! } sourceObservable.subscribe() { e in print("observer thread: \(NSThread.currentThread())") } } example("from another queue") { print("init thread: \(NSThread.currentThread())") let source = NSTextField() let status = NSTextField() let URL = NSURL(string: "https://github.com/")! let request = NSURLRequest(URL: URL) let observable = NSURLSession.sharedSession().rx_response(request).shareReplayLatestWhileConnected() let sourceObservable = observable.map { (maybeData, response) in return String(data: maybeData, encoding: NSUTF8StringEncoding)! } let queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) dispatch_async(queue1,{ print("queue1 thread: \(NSThread.currentThread())") sourceObservable.subscribe() { e in print("observer thread: \(NSThread.currentThread())") } }) }
--- from main thread example --- init thread: <NSThread: 0x7fc298d12ec0>{number = 1, name = main} curl -X GET "https://github.com/" -i -v Success (944ms): Status 200 observer thread: <NSThread: 0x7fc298fbf1a0>{number = 3, name = (null)} observer thread: <NSThread: 0x7fc298fbf1a0>{number = 3, name = (null)} --- from another queue example --- init thread: <NSThread: 0x7ff182d12ef0>{number = 1, name = main} queue1 thread: <NSThread: 0x7ff182d5c3e0>{number = 3, name = (null)} curl -X GET "https://github.com/" -i -v Success (956ms): Status 200 observer thread: <NSThread: 0x7ff185025950>{number = 4, name = (null)} observer thread: <NSThread: 0x7ff185025950>{number = 4, name = (null)}
public func rx_response(request: NSURLRequest) -> Observable<(NSData, NSHTTPURLResponse)> { return Observable.create { observer in print("RXRESPONSE thread: \(NSThread.currentThread())") ...... let task = self.dataTaskWithRequest(request) { (data, response, error) in print("TASK thread: \(NSThread.currentThread())")
disposeButton.rx_tap.debug("rx_tap") .flatMap{ value in return Observable<String>.create{ observer in observer.on(.Next("1")) observer.onError(RxError.ArgumentOutOfRange) return NopDisposable.instance } } .bindTo(a.rx_text) .addDisposableTo(disposeBag)
2016-04-30 02:02:41.486: rx_tap -> subscribed
2016-04-30 02:02:48.248: rx_tap -> Event Next(()) Binding error to UI: Argument out of range. 2016-04-30 02:02:48.248: rx_tap -> disposed
let safeSequence = xs .observeOn(MainScheduler.instance) // observe events on main scheduler .catchErrorJustReturn(onErrorJustReturn) // can't error out .shareReplayLatestWhileConnected // side effects sharing return Driver(raw: safeSequence) // wrap it up
let viewModel = GithubSignupViewModel1( input: ( username: usernameOutlet.rx_text.asObservable(), password: passwordOutlet.rx_text.asObservable(), repeatedPassword: repeatedPasswordOutlet.rx_text.asObservable(), loginTaps: signupOutlet.rx_tap.asObservable() ) ...
let viewModel = GithubSignupViewModel2( input: ( username: usernameOutlet.rx_text.asDriver(), password: passwordOutlet.rx_text.asDriver(), repeatedPassword: repeatedPasswordOutlet.rx_text.asDriver(), loginTaps: signupOutlet.rx_tap.asDriver() ), ...
viewModel.signupEnabled .subscribeNext { [weak self] valid in self?.signupOutlet.enabled = valid self?.signupOutlet.alpha = valid ? 1.0 : 0.5 } .addDisposableTo(disposeBag) viewModel.validatedUsername .bindTo(usernameValidationOutlet.ex_validationResult) .addDisposableTo(disposeBag)
viewModel.signupEnabled .driveNext { [weak self] valid in self?.signupOutlet.enabled = valid self?.signupOutlet.alpha = valid ? 1.0 : 0.5 } .addDisposableTo(disposeBag) viewModel.validatedUsername .drive(usernameValidationOutlet.ex_validationResult) .addDisposableTo(disposeBag)
validatedUsername = input.username .flatMapLatest { username in return validationService.validateUsername(username) .observeOn(MainScheduler.instance) .catchErrorJustReturn(.Failed(message: "Error contacting server")) } .shareReplay(1)
validatedUsername = input.username .flatMapLatest { username in return validationService.validateUsername(username) .asDriver(onErrorJustReturn: .Failed(message: "Error contacting server")) }
private extension UILabel { var rx_driveCoordinates: AnyObserver<CLLocationCoordinate2D> { return UIBindingObserver(UIElement: self) { label, location in label.text = "Lat: \(location.latitude)\nLon: \(location.longitude)" }.asObserver() } }
var rx_driveCoordinates: AnyObserver<CLLocationCoordinate2D> { let observer = UIBindingObserver(UIElement: self) { label, location in label.text = "Lat: \(location.latitude)\nLon: \(location.longitude)" } return observer.asObserver() }
var rx_driveCoordinates: AnyObserver<CLLocationCoordinate2D> { let uiBindingObserver: UIBindingObserver<UILabel, CLLocationCoordinate2D> = UIBindingObserver(UIElement: self) { label, location in label.text = "Lat: \(location.latitude)\nLon: \(location.longitude)" } return uiBindingObserver.asObserver() }
var rx_wasError: AnyObserver<Bool> { return UIBindingObserver(UIElement: self) { label, error in label.textColor = error ? UIColor.redColor() : UIColor.blackColor() }.asObserver() }
var rx_driveCoordinatesUIB: UIBindingObserver<UILabel, CLLocationCoordinate2D> { return UIBindingObserver(UIElement: self) { label, location in label.text = "Lat: \(location.latitude)\nLon: \(location.longitude)" } }
private extension UIView { var rx_driveAuthorization: AnyObserver<Bool> { return UIBindingObserver(UIElement: self) { view, authorized in if authorized { view.hidden = true view.superview?.sendSubviewToBack(view) } else { view.hidden = false view.superview?.bringSubviewToFront(view) } }.asObserver() } }
extension UIButton { /** Reactive wrapper for `TouchUpInside` control event. */ public var rx_tap: ControlEvent<Void> { return rx_controlEvent(.TouchUpInside) } }
public func rx_controlEvent(controlEvents: UIControlEvents) -> ControlEvent<Void> { let source: Observable<Void> = Observable.create { [weak self] observer in MainScheduler.ensureExecutingOnScheduler() // Main guard let control = self else { // - .Competed observer.on(.Completed) return NopDisposable.instance } // , ControlTarget , callback let controlTarget = ControlTarget(control: control, controlEvents: controlEvents) { control in observer.on(.Next()) } return AnonymousDisposable { controlTarget.dispose() } }.takeUntil(rx_deallocated) // return ControlEvent(events: source) }
class GeolocationViewController: ViewController { @IBOutlet weak private var button: UIButton! ... override func viewDidLoad() { ... button.rx_tap .bindNext { [weak self] in self?.openAppPreferences() } .addDisposableTo(disposeBag) ... } ... }
public func bindNext(onNext: E -> Void) -> Disposable { return subscribe(onNext: onNext, onError: { error in let error = "Binding error: \(error)" #if DEBUG rxFatalError(error) #else print(error) #endif }) }
extension UITextField { /** Reactive wrapper for `text` property. */ public var rx_text: ControlProperty<String> { return UIControl.rx_value( self, getter: { textField in textField.text ?? "" }, setter: { textField, value in textField.text = value } ) } }
static func rx_value<C: AnyObject, T: Equatable>(control: C, getter: (C) -> T, setter: (C, T) -> Void) -> ControlProperty<T> { let source: Observable<T> = Observable.create { [weak weakControl = control] observer in guard let control = weakControl else { // - .Competed observer.on(.Completed) return NopDisposable.instance } observer.on(.Next(getter(control))) // getter' // , ControlTarget let controlTarget = ControlTarget(control: control as! UIControl, controlEvents: [.AllEditingEvents, .ValueChanged]) { _ in if let control = weakControl { observer.on(.Next(getter(control))) } } return AnonymousDisposable { controlTarget.dispose() } } .distinctUntilChanged() // .takeUntil((control as! NSObject).rx_deallocated) // // , UIBindingObserver Observable setter let bindingObserver = UIBindingObserver(UIElement: control, binding: setter) return ControlProperty<T>(values: source, valueSink: bindingObserver) } }
@IBOutlet var a: NSTextField! @IBOutlet var b: NSTextField! @IBOutlet var c: NSTextField! ... override func viewDidLoad() { ... // ControlProperty Observable let sum = Observable.combineLatest(a.rx_text, b.rx_text) { (a: String, b: String) -> (Int, Int) in return (Int(a) ?? 0, Int(b) ?? 0) } ... sum .map { (a, b) in return "\(a + b)" } .bindTo(c.rx_text) // Observer' .addDisposableTo(disposeBag) }
infix operator <-> { } func <-> <T>(property: ControlProperty<T>, variable: Variable<T>) -> Disposable { let bindToUIDisposable = variable.asObservable() .bindTo(property) let bindToVariable = property .subscribe(onNext: { n in variable.value = n }, onCompleted: { bindToUIDisposable.dispose() }) return StableCompositeDisposable.create(bindToUIDisposable, bindToVariable) }
let textViewValue = Variable("") textView.rx_text <-> textViewValue
public var rx_cancelButtonClicked: ControlEvent<Void> { let source: Observable<Void> = rx_delegate.observe(#selector(UISearchBarDelegate.searchBarCancelButtonClicked(_:))) .map { _ in return () } return ControlEvent(events: source) }
searchBar.rx_cancelButtonClicked.subscribeNext { _ in // }
class SimpleTableViewExampleViewController : ViewController { @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() // Observable let items = Observable.just([ "First Item", "Second Item", "Third Item" ]) // tableView ( dataSource), items .bindTo(tableView.rx_itemsWithCellIdentifier("Cell", cellType: UITableViewCell.self)) { (row, element, cell) in cell.textLabel?.text = "\(element) @ row \(row)" } .addDisposableTo(disposeBag) // , rx_modelSelected - tableView:didSelectRowAtIndexPath: tableView .rx_modelSelected(String) .subscribeNext { value in DefaultWireframe.presentAlert("Tapped `\(value)`") } .addDisposableTo(disposeBag) // - tableView(_:accessoryButtonTappedForRowWithIndexPath:) tableView .rx_itemAccessoryButtonTapped .subscribeNext { indexPath in DefaultWireframe.presentAlert("Tapped Detail @ \(indexPath.section),\(indexPath.row)") } .addDisposableTo(disposeBag) } }
class SimpleTableViewExampleSectionedViewController : ViewController , UITableViewDelegate { @IBOutlet weak var tableView: UITableView! let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, Double>>() override func viewDidLoad() { super.viewDidLoad() let dataSource = self.dataSource 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: "Second section", items: [ 1.0, 2.0, 3.0 ]) ]) dataSource.configureCell = { (_, tv, indexPath, element) in let cell = tv.dequeueReusableCellWithIdentifier("Cell")! cell.textLabel?.text = "\(element) @ row \(indexPath.row)" return cell } items .bindTo(tableView.rx_itemsWithDataSource(dataSource)) .addDisposableTo(disposeBag) tableView .rx_itemSelected .map { indexPath in return (indexPath, dataSource.itemAtIndexPath(indexPath)) } .subscribeNext { indexPath, model in DefaultWireframe.presentAlert("Tapped `\(model)` @ \(indexPath)") } .addDisposableTo(disposeBag) tableView .rx_setDelegate(self) .addDisposableTo(disposeBag) } func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let label = UILabel(frame: CGRect.zero) label.text = dataSource.sectionAtIndex(section).model ?? "" return label } }
public func tableView(tableView: UITableView, observedEvent: Event<Element>) { UIBindingObserver(UIElement: self) { dataSource, element in dataSource.setSections(element) tableView.reloadData() }.on(observedEvent) }
class TablViewControllerEditPartialUpdate : ViewController { @IBOutlet weak var tableView: UITableView! var sections = Variable([NumberSection]()) override func viewDidLoad() { super.viewDidLoad() // NumberSection - typealias AnimatableSectionModel<String, Int> let items = [ NumberSection(model: "Section 1", items: [1, 3, 5]), NumberSection(model: "Section 2", items: [2, 4, 6, 8]), NumberSection(model: "Section 3", items: [7, 11, 10]) ] self.sections.value = items let editableDataSource = RxTableViewSectionedAnimatedDataSource<NumberSection>() configDataSource(editableDataSource) // rx_itemsAnimatedWithDataSource, rx_itemsWithDataSource self.sections.asObservable() .bindTo(tableView.rx_itemsAnimatedWithDataSource(editableDataSource)) .addDisposableTo(disposeBag) // tableView.rx_itemDeleted.subscribeNext{[weak self] item in if let controller = self { controller.sections.value[item.section].items.removeAtIndex(item.row) } }.addDisposableTo(disposeBag) // tableView .rx_modelSelected(IdentifiableValue<Int>) .subscribeNext { i in DefaultWireframe.presentAlert("Tapped `\(i)`") } .addDisposableTo(disposeBag) // NSIndexPath , tableView .rx_itemSelected .subscribeNext { [weak self] i in if let controller = self { print("Tapped `\(i)` - \(controller.sections.value[i.section].items[i.row].dynamicType)") } } .addDisposableTo(disposeBag) } func configDataSource(dataSource: RxTableViewSectionedDataSource<NumberSection>) { dataSource.configureCell = { (_, tv, ip, i) in let cell = tv.dequeueReusableCellWithIdentifier("Cell") ?? UITableViewCell(style:.Default, reuseIdentifier: "Cell") cell.textLabel!.text = "\(i)" return cell } dataSource.titleForHeaderInSection = { (ds, section: Int) -> String in return dataSource.sectionAtIndex(section).model } dataSource.canEditRowAtIndexPath = { (ds, ip) in return true } } }
fatal error: Failure converting from <RxExample_iOS.TableViewControllerEditPartialUpdate: 0x7f895a643dd0> to UITableViewDataSource: file /Users/SparkLone/projects/repos/RxSwift/RxCocoa/Common/RxCocoa.swift, line 340
rx_modelSelected(IdentifiableValue<Int>)
rx_modelSelected(Int)
fatal error: Failure converting from 4 to Int: file /Users/SparkLone/projects/repos/RxSwift/RxCocoa/Common/RxCocoa.swift, line 340
tableView .rx_itemSelected .subscribeNext { [weak self] i in if let controller = self { print("Tapped `\(i)` - \(controller.sections.value[i.section].items[i.row].dynamicType)") } } .addDisposableTo(disposeBag)
precondition failed: Item 3 has already been indexed at (0, 1): file /Users/SparkLone/projects/repos/RxSwift/RxExample/RxDataSources/DataSources/Differentiator.swift, line 130
protocol DelegateProxyType { // static func createProxyForObject(object: AnyObject) -> AnyObject // objc_setAssociatedObject static func assignProxy(proxy: AnyObject, toObject object: AnyObject) // objc_getAssociatedObject static func assignedProxyFor(object: AnyObject) -> AnyObject? // / ( Rx) func setForwardToDelegate(forwardToDelegate: AnyObject?, retainDelegate: Bool) func forwardToDelegate() -> AnyObject? // / -, static func currentDelegateFor(object: AnyObject) -> AnyObject? static func setCurrentDelegate(delegate: AnyObject?, toObject object: AnyObject) }
class DelegateProxy { public class func createProxyForObject(object: AnyObject) -> AnyObject {} public class func assignedProxyFor(object: AnyObject) -> AnyObject? {} public class func assignProxy(proxy: AnyObject, toObject object: AnyObject) {} public func setForwardToDelegate(delegate: AnyObject?, retainDelegate: Bool) {} public func forwardToDelegate() -> AnyObject? {} }
extension UISearchController { // , RxSearchControllerDelegateProxy public var rx_delegate: DelegateProxy { return proxyForObject(RxSearchControllerDelegateProxy.self, self) } // Rx UISearchControllerDelegate.didDismissSearchController(_:) public var rx_didDismiss: Observable<Void> { return rx_delegate .observe(#selector(UISearchControllerDelegate.didDismissSearchController(_:))) .map {_ in} } ... }
public class RxSearchControllerDelegateProxy : DelegateProxy , DelegateProxyType , UISearchControllerDelegate { // ( ) (UISearchController) (delegate) public class func setCurrentDelegate(delegate: AnyObject?, toObject object: AnyObject) { let searchController: UISearchController = castOrFatalError(object) searchController.delegate = castOptionalOrFatalError(delegate) } // , public class func currentDelegateFor(object: AnyObject) -> AnyObject? { let searchController: UISearchController = castOrFatalError(object) return searchController.delegate } }
proxyForObject(RxSearchControllerDelegateProxy.self, self)
public func proxyForObject<P: DelegateProxyType>(type: P.Type, _ object: AnyObject) -> P { MainScheduler.ensureExecutingOnScheduler() // let maybeProxy = P.assignedProxyFor(object) as? P // assignedProxyFor DelegateProxy let proxy: P if maybeProxy == nil { proxy = P.createProxyForObject(object) as! P // ( RxSearchControllerDelegateProxy). createProxyForObject DelegateProxy , , P.assignProxy(proxy, toObject: object) // . assignProxy DelegateProxy, , assignedProxyFor assert(P.assignedProxyFor(object) === proxy) } else { proxy = maybeProxy! // - } let currentDelegate: AnyObject? = P.currentDelegateFor(object) // UI ( delegate/dataSource). DelegateProxy , .. as! if currentDelegate !== proxy { // proxy.setForwardToDelegate(currentDelegate, retainDelegate: false) // . UI . Objective-C Rx, _RXDelegateProxy. P.setCurrentDelegate(proxy, toObject: object) // DelegateProxy , .. as! assert(P.currentDelegateFor(object) === proxy) assert(proxy.forwardToDelegate() === currentDelegate) } return proxy }
extension UIScrollView { /** Factory method that enables subclasses to implement their own `rx_delegate`. - returns: Instance of delegate proxy that wraps `delegate`. */ public func rx_createDelegateProxy() -> RxScrollViewDelegateProxy { return RxScrollViewDelegateProxy(parentObject: self) } /** Reactive wrapper for `delegate`. For more information take a look at `DelegateProxyType` protocol documentation. */ public var rx_delegate: DelegateProxy { return proxyForObject(RxScrollViewDelegateProxy.self, self) } ... }
public class RxScrollViewDelegateProxy : DelegateProxy , UIScrollViewDelegate , DelegateProxyType { public override class func createProxyForObject(object: AnyObject) -> AnyObject { let scrollView = (object as! UIScrollView) return castOrFatalError(scrollView.rx_createDelegateProxy()) } ... }
extension UITableView { /** Factory method that enables subclasses to implement their own `rx_delegate`. - returns: Instance of delegate proxy that wraps `delegate`. */ public override func rx_createDelegateProxy() -> RxScrollViewDelegateProxy { return RxTableViewDelegateProxy(parentObject: self) } ... }
public class RxTableViewDelegateProxy : RxScrollViewDelegateProxy , UITableViewDelegate { public weak private(set) var tableView: UITableView? public required init(parentObject: AnyObject) { self.tableView = (parentObject as! UITableView) super.init(parentObject: parentObject) } }
Source: https://habr.com/ru/post/283128/
All Articles