Rx in the real world 1 Rob Ciolli
2 Rob Ciolli
3 Rob Ciolli
The App 4 Rob Ciolli
Quick architecture overview 5 Rob Ciolli
MV - WTF 6 Rob Ciolli
Model Simple, immutable data struct returned from DB or APIs 7 Rob Ciolli
View UI layout as defined in storyboard file 8 Rob Ciolli
Controller Regular Cocoa ViewController protocol ViewControllerProtocol: class {... associatedtype ViewModelType func recieve(viewmodel: ViewModelType) 9 Rob Ciolli
Presenter import UIKit protocol PresenterProtocol { associatedtype ViewControllerType: UIViewController func makeviewcontroller() -> ViewControllerType 10 Rob Ciolli
ViewModel protocol ViewModelDelegateProtocol { protocol ViewModelProtocol { associatedtype DelegateType //: ViewModelDelegateProtocol var delegate: DelegateType { get init(delegate: DelegateType) class BaseViewModel<T> : ViewModelProtocol { let delegate: T required init(delegate: T) { self.delegate = delegate 11 Rob Ciolli
Example 1 Beer Detail Page 12 Rob Ciolli
Beer Model struct Beer { let id: Int let name: String let style: String let brewry: String let abv: Double let ibu: Double let description: String let image: String 13 Rob Ciolli
Observable next complete error 14 Rob Ciolli
Observer 'Listens' to an Observable implements onnext, oncompleted, onerror subscribing or binding returns a Disposable DisposeBag pattern 15 Rob Ciolli
Observable example let observable = Observable<String>.create { observer in o.onnext("beer") o.onnext("is") o.onnext("good") o.oncompleted() return Disposables.create() 16 Rob Ciolli
Observer example let disposebag = DisposeBag() observable.subscribe( onnext: { s in print(s), onerror: { _ in print("wtf"), oncompleted: { _ in print("done") ).disposed(by: disposebag) 17 Rob Ciolli
Mutating the streams Observable Operators Transform => Map & FlatMap... Filter => Filter / Debounce / Skip / Take Combine => Zip / CombineLatest Error Handling => Retry / Catch 18 Rob Ciolli
Data Layer import RxSwift protocol Datalayer { func requestallbeers() -> Observable<[Beer]> 19 Rob Ciolli
DetailPresenter struct DetailPresenter { let beer: Beer init(beer: Beer) { self.beer = beer extension DetailPresenter: PresenterProtocol {... extension DetailPresenter: DetailViewModelDelegateProtocol { 20 Rob Ciolli
DetailViewModel protocol DetailViewModelDelegateProtocol: ViewModelDelegateProtocol { var beer: Beer { get class DetailViewModel: BaseViewModel<DetailViewModelDelegateProtocol> { let name = Variable<String?>("")... required init(delegate: DetailViewModelDelegateProtocol) { super.init(delegate: delegate) name.value = beer.name... 21 Rob Ciolli
Map 22 Rob Ciolli
DetailViewController class DetailViewController: UIViewController, ViewControllerProtocol {... let disposebag = DisposeBag() @IBOutlet weak var namelabel: UILabel! @IBOutlet weak var imageview: UIImageView! override func viewdidload() { super.viewdidload() viewmodel.name.asobservable().bindto(namelabel.rx.text).disposed(by: disposebag) viewmodel.image.asobservable().map { UIImage(named: $0)!.bindTo(imageView.rx.image).disposed(by: disposebag) 23 Rob Ciolli
Show me the code Example 1 - Beer Detail Page 24 Rob Ciolli
! " Binding is too verbose Only displays one piece of data Not really reactive 25 Rob Ciolli
Example 1b Beer Detail Page (again...) heaps more reactive 26 Rob Ciolli
Binding 27 Rob Ciolli
what if... viewmodel.name.asobservable().bindto(namelabel.rx.text) looked like namelabel.rx.text <- viewmodel.name 28 Rob Ciolli
... and you could add all disposables to disposebag in one call... disposebag.dispose([ namelabel.rx.text <- viewmodel.name, stylelabel.rx.text <- viewmodel.style, brewrylabel.rx.text <- viewmodel.brewry, ]) 29 Rob Ciolli
<- operator infix operator <- func <- <T>(property: ControlProperty<T>, variable: Variable<T>) -> Disposable { return variable.asobservable().bind(to: property) 30 Rob Ciolli
DisposeBag extension DisposeBag { func dispose(_ disposables: [Disposable]) { disposables.foreach { [unowned self] disposable in self.insert(disposable) 31 Rob Ciolli
ViewModel Requirements Page through [Beer] protocol DetailViewModelDelegateProtocol: ViewModelDelegateProtocol { var beers: Observable<[Beer]> { get... 32 Rob Ciolli
ViewModel Requirements React to Next/Prev let next = Variable<()>() let index: Variable<Int>... next.asobservable().subscribe(onnext: onnext)... private func onnext() { index.value += 1 33 Rob Ciolli
Combine Latest 34 Rob Ciolli
ViewModel Requirements Be heaps more reactive Observable.combineLatest(delegate.beers, index.asobservable(), resultselector: selectbeer) 35 Rob Ciolli
ViewModel Requirements Control enabled state of UIButton extension UIButton { var rx_driveenable: AnyObserver<Bool> { return UIBindingObserver(UIElement: self) { button, enabled in button.isuserinteractionenabled = enabled.asobserver() 36 Rob Ciolli
Show me the code (... and tests) Example 1b - Beer Detail Page 37 Rob Ciolli
Example 2 Sign in Page capture user input validate input call api and handle response 38 Rob Ciolli
Sign in ViewModel initialise protocol SigninViewModelDelegateProtocol: ViewModelDelegateProtocol { func signin(username: String, password: String) -> Observable<Void> class SigninViewModel: BaseViewModel<SigninViewModelDelegateProtocol> {... let username = Variable<String?>(nil) let password = Variable<String?>(nil) let signintapped = Variable<()>() let error = Variable<String?>(nil) let signedin = Variable<()>() 39 Rob Ciolli
Sign in ViewModel call api...... signintapped.asobservable().subscribe(onnext: signin).disposed(by: disposebag) private func signin() { guard let username = username.value, let password = password.value else { return delegate.signin(username: username, password: password).subscribe(onnext: onsignedin, onerror: onsigninerror).disposed(by: disposebag) private func onsignedin() { signedin.value = () private func onsigninerror(_: Error) { error.value = "Error signing in" 40 Rob Ciolli
Sign in ViewModel input validation var signinenabled: Observable<Bool> { return Observable.combineLatest(username.asObservable(), password.asobservable(), resultselector: inputisvalid) private func inputisvalid(username: String?, password:string?) -> Bool { guard let username = username, let password = password else { return false return!username.isempty &&!password.isempty 41 Rob Ciolli
Show me the code Example 2 - Beer Sign in 42 Rob Ciolli
Example 3 Beer ListView filter list show detail view 43 Rob Ciolli
List ViewModel let searchvariable = Variable<String?>("") let itemtapped = Variable<Int>(-1) required init(delegate: ListViewModelDelegateProtocol) {... itemtapped.asobservable().skip(1).subscribe(onnext: onitemtapped).disposed(by: disposebag) 44 Rob Ciolli
Filter 45 Rob Ciolli
List ViewModel func filteredbeers() -> Observable<[Beer]> { let search = searchvariable.asobservable() return Observable.combineLatest(delegate.beers, search, resultselector: filterbeers) private func filterbeers(beers: [Beer], search: String?) -> [Beer] { guard let search = search else { return beers return beers.filter { search.isempty $0.name.lowercased().contains(search.lowercased()) 46 Rob Ciolli
List ViewModel private func onitemtapped(index: Int) { delegate.showdetail(beers: filteredbeers(), index: index) 47 Rob Ciolli
List ViewController viewmodel.filteredbeers().debounce(0.3, scheduler: MainScheduler.instance).bind(to: tableview.rx.items( cellidentifier: "Cell", celltype: UITableViewCell.self), curriedargument: initialisecell) 48 Rob Ciolli
Show me the code Example 3 - Beer List 49 Rob Ciolli
Wash up single responsibility is good Rx makes us think about data flow infix operators and swift extensions are cool 50 Rob Ciolli
Resources http: /reactivex.io/ http: /rxmarbles.com/ https: /github.com/reactivex/rxswift 51 Rob Ciolli
Thank you @boblaroc <- (, ) 52 Rob Ciolli