Save 37% off PRO during our Black Friday Sale! »

RxSwift: an elegant weapon for a statically typed age

RxSwift: an elegant weapon for a statically typed age

Reactive Extensions are hardly a new concept, but it took some time to enter mainstream iOS development. For all of you wondering "is it worth the hype", here comes the RxSwift primer. We'll learn what the Observable is all about and how to use it without shooting ourselves in the foot. Why would you want to incorporate RxSwift into your project? Come and see!

43d2bef703ec7165166f161f137ac54f?s=128

Krzysztof Siejkowski

April 11, 2016
Tweet

Transcript

  1. RXSWIFT AN ELEGANT WEAPON FOR A STATICALLY-TYPED AGE BY KRZYSZTOF

    SIEJKOWSKI
  2. ✨ WHAT'S RXSWIFT?

  3. REACTIVE EXTENSIONS

  4. OBSERVABLE GIVES DATA OBSERVER TAKES DATA

  5. DATA ➡ NEXT[VALUE]* (COMPLETED | ERROR) Next("3"), Next("2"), Next("1"), Next("!"),

    Completed Next("The Spanish Inquisition"), Error(NobodyExpects) Error(RequestTimeout) Completed
  6. public enum Event<Element> { case Next(Element) case Error(ErrorType) case Completed

    } protocol ObserverType { associatedtype Element } protocol ObservableType { associatedtype Element }
  7. "a combination of the best ideas from the Observer pattern,

    the Iterator pattern, and functional programming" — reactivex.io
  8. FROM OBSERVER PATTERN let notificationCenter : NSNotificationCenter notificationCenter.addObserverForName("ButtonTapped", object: nil,

    queue: nil) { (notification: NSNotification) in // observer closure } let observable: Observable<ButtonTappedEvent> observable.subscribeNext { (event: ButtonTappedEvent) in // observer closure }
  9. FROM ITERATOR PATTERN let generator = [1, 2, 3, 4,

    5, 6].generate() while let elem = generator.next() { // elem is pulled from sequence } let observer = AnyObserver { event in // event is pushed by observable } [1, 2, 3, 4, 5, 6].asObservable().subscribe(observer)
  10. FROM FUNCTIONAL PROGRAMMING [1, 2, 3, 4, 5, 6].filter {

    ... } .map { ... } .flatMap { ... } [1, 2, 3, 4, 5, 6].asObservable() .filter { ... } .map { ... } .flatMap { ... }
  11. OBSERVABLE PUBLISHES AND TRANSFORMS EVENTS OBSERVER RECEIVES EVENTS

  12. THE REST IS UP TO YOU

  13. PROTOCOL UIVIEW OBSERVABLE

  14. ✨ HOW TO USE RXSWIFT?

  15. OPERATORS

  16. REACTIVEX.IO/DOCUMENTATION/ OPERATORS.HTML GITHUB.COM/REACTIVEX/RXSWIFT/BLOB/ MASTER/DOCUMENTATION/API.MD

  17. CREATE FROM EXISTING OBJECT ["Mobile", "Warsaw"].asObservable() textField.rx_text.asObservable() NSURLSession.sharedSession().rx_JSON(request)

  18. CREATE WITH HELPER FUNCTIONS Observable<Int>.interval(1, scheduler: MainScheduler.instance) Observable.repeatElement("Did you miss

    me?") Observable.error(CouldHaveGoneBetter()) let v = Variable("Riding to Valhalla") v.asObservable() v.value = "Witness me!"
  19. CREATE WITH CUSTOM LOGIC Observable<String>.create { observer in observer.onNext("keep calm")

    observer.onNext("and") observer.onNext("stay observable") observer.onCompleted() return NopDisposable.instance } ONLY ON VERY SPECIAL OCCASIONS
  20. COMBINE: RXMARBLES.COM observable.concat(secondObservable) Observable.combineLatest(observable1, observable2, observable3) { elem1, elem2, elem3

    in elem1 + elem2 + elem3 } Observable.zip(observable1, observable2) { elem1, elem2 in (elem1, elem2) }
  21. PERFORM WORK IN DATA PIPES observable .distinctUntilChanged() .filter { elem

    in elem > 0 } .map { [weak self] elem in self?.performWork(elem) } .flatMap { [weak self] elem in self?.useForAsyncOperation(elem) }
  22. CONNECT EVENT STREAM TO YOUR LOGIC observable.subscribeNext { [weak self]

    elem in self?.processValue(elem) } observable.asDriver(onErrorJustReturn: "sorry!") .drive(label.rx_text)
  23. RECAP > observable ➡ events ➡ observer > observer ❤

    iterator ❤ functional programming > customizable building block " > operators #
  24. ! DON'T SHOOT YOURSELF IN THE FOOT

  25. Any sufficiently advanced technology is indistinguishable from magic. — Arthur

    C. Clarke BUT IT'S NOT MAGIC!
  26. ! MEMORY MANAGEMENT

  27. USER-GENERATED EVENTS let disposeBag = DisposeBag() let gestureRecognizer = UITapGestureRecognizer()

    gestureRecognizer.rx_event .subscribeNext { [weak self] _ in self?.processDetectedTap() } .addDisposableTo(disposeBag) view.addGestureRecognizer(gestureRecognizer)
  28. CLEAN UP! let disposeBag = DisposeBag() let subscription = observable.subscribe(observer)

    subscription.addDisposableTo(disposeBag) ... disposeBag = nil
  29. WEAKIFY SELF IN CLOSURES class SomeClass { func someMethod(param: Int)

    { ... } func otherMethod -> Observable<Void> { return observable.just(1) .map(someMethod) } } return observable.just(1).map { [weak self] elem in self?.someMethod(elem) ?? 42 }
  30. ! ASYNCHRONOUS EXECUTION

  31. NETWORK CALL class NetworkClient { func fetchSomeNews() -> Observable<News> {

    return NSURLSession.sharedSession() .rx_JSON("https://my.server.com/api/v2/news") .observeOn(SerializationScheduler.instance) .map { [weak self] json in self?.newsDeserializer.deserialize(json) } } }
  32. NETWORK CALL self.networkClient.fetchSomeNews() .observeOn(MainScheduler.instance) .subscribeNext { [weak self] news in

    self?.newsView.presentNews(news) } .addDisposableTo(disposeBag)
  33. OBSERVABLE IS SINGLE-THREADED

  34. DECLARATION THREAD subscribe GENERATION THREAD subscribeOn & CREATE METHODS WORK

    THREAD(S) observeOn
  35. 1. Declaration thread until told otherwise. 2. Generation thread until

    changed. 3. Only first generation thread change counts. 4. No limit on work thread changes.
  36. ! STATE

  37. MODEL-UI BINDING class MyViewController { func bindUserData() { let observable

    = self.userService.provideUserName() .observeOn(MainScheduler.instance) .shareReplay(1) observable.bindTo(self.userDataLabel.rx_text) .addDisposableTo(self.disposeBag) observable.subscribeNext { [weak self] userName in self?.navigationItem.title = userName }.addDisposableTo(self.disposeBag) } }
  38. OBSERVABLE DOESN'T REMEMBER EVENTS let observable = Observable.just(1) .map {

    _ in print("I'm in map!") } observable.subscribeNext { _ in print("first") } observable.subscribeNext { _ in print("second") } // I'm in map! // first // I'm in map // second
  39. UNTIL EXPLICITLY TOLD SO let observable = Observable.just(1) .map {

    _ in print("I'm in map!") } .shareReplay(1) observable.subscribeNext { _ in print("first") } observable.subscribeNext { _ in print("second") } // I'm in map! // first // second
  40. ! READABILITY

  41. almost every single developer that I talked to uses strong

    and inflammatory language about its unreadability. — Brent Simmons, Reactive Followup
  42. EXTRACT LOGIC NSURLSession.sharedSession().rx_response(myNSURLRequest) .flatMap { (data: NSData!, response: NSURLResponse!) ->

    Observable<String> in guard let response = response as? NSHTTPURLResponse else { return Observable.error(yourNSError) } if 200 ..< 300 ~= response.statusCode { return just(transform(data)) } else { return Observable.error(yourNSError) } } NSURLSession.sharedSession().rx_response(myNSURLRequest) .flatMap { [weak self] data, response in self?.processResponse(data, response) }
  43. USE PROVIDED HELPERS AND CREATE YOUR OWN observable.just("they see me

    rollin'") .catchError { _ in Observable.empty() } .observeOn(MainScheduler.instance) .shareReplayLatestWhileConnected() observable.just("they hatin'") .asDriver { _ in Driver.empty() }
  44. RECAP > manage memory ! > identify the threads "

    > know when to share work # > good practices apply! $
  45. ! ALL THINGS OBSERVABLE

  46. OBSERVABLES SCALE > for networking? > for asynchronous operations? >

    for updating model and driving ui? > for communication between layers? > for all logic?
  47. OBSERVABLES EXPRESS DATA FLOW fetchDataFromServer() .observeOn(BackgroundScheduler.instance) .map { deserializeFromJson() }

    .flatMap { decideOnCaching() } .map { performComputation() } .flatMap { updateModel() } .asDriver { _ in provideDefaultValue() } .driveNext { updateUI() } Advanced iOS Application Architecture and Patterns
  48. OBSERVABLES REPLACE func optionalToObservable<T>(optional: T?) -> Observable<T> { return Observable.just(optional).flatMap

    { elem -> Observable<T> in if let optional = optional { return Observable.just(optional) } else { return Observable.error(NilError()) } } } optional, result, future, throws, sequencetype, ... KVO, NSNotificationCenter, target-action, callbacks, GCD, ...
  49. OBSERVABLES UNIFORM func fun(observable: Observable<Int>) -> Observable<String> init<T, E :

    ObserverType where E.E = T>( dataObservable: Observable<T>, dataObserver: E ) { ... }
  50. SWIFTY id

  51. LEARN ONCE, WRITE EVERYWHERE > ReactiveCocoa / Interstellar > RxJava

    / RxKotlin / RxGroovy > RxScala / RxClojure > RxPy / Rx.rb / RxJRuby > RxCpp / Rx.NET / UniRx > RxJS / Elm
  52. DEVELOPER FRIENDLY guides at guides.rxswift.org docs at reactivex.io source at

    github.com/ReactiveX/RxSwift extensions at community.rxswift.org community at slack.rxswift.org
  53. RECAP > use a little or a lot ! >

    uniform various concepts " > express data flow # > check the docs $ > consult the community %
  54. THANK YOU! QUESTIONS?