Slide 1

Slide 1 text

RxSwift RxCocoa

Slide 2

Slide 2 text

About me @stefanscheidt Software Engineer @ REWE digital mostly Java, some JavaScript, iOS & Swift Newbie

Slide 3

Slide 3 text

Asynchronous Programming with Observable Streams for Swift and Cocoa

Slide 4

Slide 4 text

Reactive Extentions History • 2009: Rx.NET • 2010: RxJS • 2012: RxJava, RxCpp, RxRuby • 2013: RxScala, RxClojure, RxKotlin, RxPY ... (ReactiveCocoa) • 2015: RxSwift

Slide 5

Slide 5 text

RxSwift Fact Sheet • Swift Support: 2.3, 3.0 (Beta) • Code Quality: see CocoaPods • Contributors & Activity: see GitHub • External Dependencies: Non • Usage via CocoaPods, Carthage, Git Submodules • Community Extentions

Slide 6

Slide 6 text

Basic Concepts

Slide 7

Slide 7 text

Observable Sequences: Push vs. Pull 1 • Observable eq. SequenceType • ObservableType.subscribe • eq. SequenceType.generate • vs. generator.next • But can also receive elements asynchronously! 1 RxSwift Getting Started

Slide 8

Slide 8 text

Events next* (error | completed)? • Sequences can have 0 or more elements • Once an error or completed event is received, the sequence cannot produce any other element

Slide 9

Slide 9 text

Examples2 Observable.of(1, 2, 3) .subscribe { print($0) } will print next(1) next(2) next(3) completed 2 using RxSwift 3.0.0-beta.1

Slide 10

Slide 10 text

Disposing release all allocated resources: Observable.of(1, 2 ,3) .subscribe { print($0) } .dispose() or better let disposeBag = DisposeBag() Observable.of(1, 2 ,3) .subscribe { print($0) } .addDisposableTo(disposeBag)

Slide 11

Slide 11 text

Operators 3 • Creating • Transforming • Filtering • Combining 3 https://github.com/ReactiveX/RxSwift/blob/master/Documentation/API.md

Slide 12

Slide 12 text

Rx Marbles http://rxmarbles.com/ https://github.com/RxSwiftCommunity/RxMarbles

Slide 13

Slide 13 text

Sequence Generation When does an Observable begin emitting its items?4 "Cold" Observable: waits until an observer subscribes "Hot" Observable: may begin emitting items as soon as created 4 See also Hot and Cold Observables

Slide 14

Slide 14 text

Cold Observables let disposeBag = DisposeBag() let observable = Observable.of(1, 2) observable.subscribe { print($0) }.addDisposableTo(disposeBag) observable.subscribe { print($0) }.addDisposableTo(disposeBag) will print next(1) next(2) completed next(1) next(2) completed

Slide 15

Slide 15 text

"Hot" Observables let pub = PublishSubject() pub.onNext(1) let sub1 = pub.subscribe { print("sub1: \($0)") } pub.onNext(2) let sub2 = pub.subscribe { print("sub2: \($0)") } pub.onNext(3) sub2.dispose() pub.onCompleted() will print sub1: next(2) sub1: next(3) sub2: next(3) sub1: completed

Slide 16

Slide 16 text

Create Observables func interval(_ interval: TimeInterval) -> Observable { return Observable.create { observer in print("Subscribed") let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global()) timer.scheduleRepeating(deadline: DispatchTime.now() + interval, interval: interval) let cancel = Disposables.create { print("Disposed") timer.cancel() } var count = 0 timer.setEventHandler { if cancel.isDisposed { return } observer.on(.next(count)) count += 1 } timer.resume() return cancel } }

Slide 17

Slide 17 text

Create Observables let counter = interval(0.5).subscribe { print($0) } Thread.sleep(forTimeInterval: 2.0) counter.dispose() will print Subscribed 0 1 2 3 Disposed

Slide 18

Slide 18 text

Cold Observables - again let counter = interval(0.1) let sub1 = counter.subscribe(onNext: { print("Sub1: \($0)") }) let sub2 = counter.subscribe(onNext: { print("Sub2: \($0)") }) Thread.sleep(forTimeInterval: 0.3) sub1.dispose() Thread.sleep(forTimeInterval: 0.3) sub2.dispose() will print ...

Slide 19

Slide 19 text

Cold Observables - again Subscribed Subscribed Sub1: 0 Sub2: 0 Sub1: 1 Sub2: 1 Sub1: 2 Sub2: 2 Disposed Sub2: 3 Sub2: 4 Disposed

Slide 20

Slide 20 text

Share Replay let counter = interval(0.1).shareReplay(1) let sub1 = counter.subscribe(onNext: { print("Sub1: \($0)") }) let sub2 = counter.subscribe(onNext: { print("Sub2: \($0)") }) Thread.sleep(forTimeInterval: 0.3) sub1.dispose() Thread.sleep(forTimeInterval: 0.3) sub2.dispose() will print ...

Slide 21

Slide 21 text

Share Replay Subscribed Sub1: 0 Sub2: 0 Sub1: 1 Sub2: 1 Sub1: 2 Sub2: 2 Sub2: 3 Sub2: 4 Sub2: 5 Disposed

Slide 22

Slide 22 text

Schedulers5 Schedulers abstract away the mechanism for performing work • CurrentThreadScheduler: serial on current thread • MainScheduler: serial on main thread • SerialDispatchQueueScheduler: serial on dispatch queue • ConcurrentDispatchQueueScheduler: concurrent on dispatch queue • OperationQueueScheduler: concurrent on operation queue 5 https://github.com/ReactiveX/RxSwift/blob/master/Documentation/Schedulers.md

Slide 23

Slide 23 text

Observe On Scheduler sequence .observeOn(backgroundScheduler) .map { _ in print("This is performed on backgroundScheduler") } .observeOn(MainScheduler.instance) .map { _ in print("This is performed on the main thread") }

Slide 24

Slide 24 text

Observe On Scheduler let scheduler = SerialDispatchQueueScheduler(internalSerialQueueName: "com.rewe-digital.rxswift.interval") let subscription = Observable.interval(0.3, scheduler: scheduler) .map { "Simply \($0)"} .subscribe(onNext: { print($0) }) Thread.sleep(forTimeInterval: 1.0) subscription.dispose() will print Simply 0 Simply 1 Simply 2

Slide 25

Slide 25 text

Debugging let scheduler = SerialDispatchQueueScheduler(internalSerialQueueName: "com.rewe-digital.rxswift.interval") let subscription = Observable.interval(0.3, scheduler: scheduler) .debug("debugging ...") .map { "Simply \($0)"} .subscribe(onNext: { print($0) }) Thread.sleep(forTimeInterval: 1.0) subscription.dispose()

Slide 26

Slide 26 text

Debugging will print 2016-09-09 16:42:29.871: debugging ... -> subscribed 2016-09-09 16:42:30.172: debugging ... -> Event next(0) Simply 0 2016-09-09 16:42:30.475: debugging ... -> Event next(1) Simply 1 2016-09-09 16:42:30.775: debugging ... -> Event next(2) Simply 2 2016-09-09 16:42:30.873: debugging ... -> disposed

Slide 27

Slide 27 text

Testing • XCTestExpectation • RxTests

Slide 28

Slide 28 text

RxSwift Examples See Rx.playground

Slide 29

Slide 29 text

RxCocoa

Slide 30

Slide 30 text

Units Important properties when writing Cocoa/UIKit applications: • Subscribe to properties, events on main thread • Observe on main thread • Share events • Don't error out

Slide 31

Slide 31 text

Units Units6 are convenient wrapper around observables for writing UI code • ControlProperty • ControlEvent • Driver 6 https://github.com/ReactiveX/RxSwift/blob/master/Documentation/Units.md

Slide 32

Slide 32 text

Why Units? let results = query.rx.text .throttle(0.3, scheduler: MainScheduler.instance) .flatMapLatest { fetchItems($0) } results.map { "\($0.count)" } .bindTo(resultCount.rx.text) .addDisposableTo(disposeBag) results.bindTo(tableView.rx.itemsWithCellIdentifier("Cell")) { (_, result, cell) in cell.textLabel?.text = "\(result)" }.addDisposableTo(disposeBag)

Slide 33

Slide 33 text

Problems with this code ... • If fetchItems errors out, everything would unbind • If it returns on some background thread, results would be bound to UI there • Results are bound to two UI elements, so two HTTP requests would be made

Slide 34

Slide 34 text

So ... let results = query.rx.text .throttle(0.3, scheduler: MainScheduler.instance) .flatMapLatest { fetchItems($0) .observeOn(MainScheduler.instance) .catchErrorJustReturn([]) }.shareReplay(1) results.map { "\($0.count)" } .bindTo(resultCount.rx.text) .addDisposableTo(disposeBag) results.bindTo(tableView.rx.itemsWithCellIdentifier("Cell")) { (_, result, cell) in cell.textLabel?.text = "\(result)" }.addDisposableTo(disposeBag)

Slide 35

Slide 35 text

Therefore: Driver let results = query.rx.text.asDriver() .throttle(0.3, scheduler: MainScheduler.instance) .flatMapLatest { fetchItems($0) .asDriver(onErrorJustReturn: []) } results.map { "\($0.count)" } .drive(resultCount.rx.text) .addDisposableTo(disposeBag) results.drive(tableView.rx.itemsWithCellIdentifier("Cell")) { (_, result, cell) in cell.textLabel?.text = "\(result)" }.addDisposableTo(disposeBag)

Slide 36

Slide 36 text

RxCocoa Examples See RxExample

Slide 37

Slide 37 text

Other options • ReactiveSwift + ReactiveCocoa • Interstellar • FutureKit • AltConf Talk by Michael Gray

Slide 38

Slide 38 text

By the way ... Observable Sequences are Monads7 7 Swift Functors, Applicatives, and Monads in Pictures

Slide 39

Slide 39 text

Thank you!8 8 Slide deck sources on GitHub