Skip to content

Latest commit

Β 

History

History
736 lines (487 loc) Β· 11.3 KB

slides.md

File metadata and controls

736 lines (487 loc) Β· 11.3 KB

Reactive Thinking in iOS Development

@pepibumur / @saky

image


Who?

@pepibumur iOS Developer at SoundCloud GitHub: pepibumur Twitter: pepibumur

@saky iOS Developer at Letgo GitHub: isaacroldan Twitter: saky

fit left

GitDo.io our spare time project


Index

  • Programming Paradigms
  • Reactive Libraries
  • Reactive Motivation
  • Reactive Thinking
  • Reactive Caveats
  • Conclusion

Paradigms πŸ“–

Ways of seeing the world when it comes to programming

fill


Data-Driven, Declarative, Dynamic, End-User, Event-Driven, Expression-Oriented, Feature-Oriented, Function-level, Generic, Imperative, Inductive, Language Oriented, Metaprogramming, Non-Structured, Nondeterministic, Parallel computing, Point-free Style, Structured, Value-Level, Probabilistic


Imperative Programming

Declarative Programming


Imperative Programming

Declarative Programming


How? πŸ€”


func userDidSearch(term: String) {
	let apiReults = api.search(term: term).execute()
	self.items = self.adaptResults(apiResults)
	self.tableView.reloadData()
}

Sequence of steps

that happen in order


Natural way to program


Execution state

(aka side effect)


Imperative Programming

Declarative Programming


What? πŸ€”


let predicate = NSPredicate(format: "name == %@", "Pedro")
let regex = NSRegularExpression(pattern: ".+", options: 0)

It doesn't describe the control flow


XML


XML

HTML


XML

HTML

SQL


XML

HTML

SQL

Reactive Programming


Asynchronous data-flow programming

Describes the state propagation

####### The introduction to Reactive Programming that you've been missing


self.fetchAccountCommandFactory.githubAccount()
  .map { $0.hasFeature(.ImagePicker) }
  .observeOn(MainScheduler.instance)
  .subscribeNext { [weak self] hasFeature in
      if hasFeature {
          self?.view?.showImagePicker()
      } else {
          self?.openSubscriptionView()
      }
  }
  .addDisposableTo(self.disposeBag)

Functional Reactive Programming

(aka FRP)


Describes how data transform from one state to another and what triggers it


State contained in a tree of transformation nodes

(functionally pure)


Reactive Libraries


- _More and more..._ [PromiseKit](https://github.com/mxcl/PromiseKit), [Bolts](https://github.com/BoltsFramework), [ReactiveKit](https://github.com/ReactiveKit/ReactiveKit)...

fill


What library should i use? πŸ˜…


What library should i use? πŸ˜…

Do i need a library for this? 😯


Swift

var userName: String {
  didSetΒ {
    // React to changes in variables

  }
}

Swift

var userName: String {
  didSetΒ {
    // React to changes in variables
    view.updateTitle(userName)
  }
}

Motivation πŸ˜‹

Why should I stick to Reactive programming?

fill


  • immutability
  • Side states safe (clear source of truth)
  • Binding
  • Encapsulated Observable actions
  • Composable
  • Ease operations
  • Threading (observer & execute)

  • immutability
  • Side states safe (clear source of truth)
  • Binding
  • Encapsulated Observable actions
  • Composable
  • Ease operations
  • Threading (observer & execute)

Binding


UI Binding


UI Binding

inline


UI Binding

inline


  • immutability
  • Side states safe (clear source of truth)
  • Binding
  • Encapsulated Observable actions
  • Composable
  • Ease operations
  • Threading (observer & execute)

inline


inline


  • immutability
  • Side states safe (clear source of truth)
  • Binding
  • Encapsulated Observable actions
  • Composable
  • Ease operations
  • Threading (observer & execute)

Threading

inline


Reactive Thinking 😬

fill


Thinking in terms of

Observables

Or Signals/Producers in ReactiveCocoa


Actions can be

observed

How? πŸ™„

fill


Operations

RxSwift

Observable<String>.create { (observer) -> Disposable in
    observer.onNext("next value")
    observer.onCompleted()
    return NopDisposable.instance // For disposing the action
}

ReactiveCocoa

SignalProducer<String>.create { (observer, disposable) in
	observer.sendNext("next value")
	observer.sendComplete()
}

Existing Patterns

RxSwift β–ΆοΈŽ RxCocoa (extensions)

ReactiveCocoa β–ΆοΈŽ DO IT YOURSELF


UIKit

let button = UIButton()
button.rx_controlEvent(.TouchUpInside)
  .subscribeNext { _ in
    print("The button was tapped")
  }

Notifications

NSNotificationCenter.defaultCenter()
	.rx_notification("my_notification", object: nil)
	.subscribeNext { notification
		// We got a notification
	}

Delegates

self.tableView.rx_delegate // DelegateProxy
  .observe(#selector(UITableViewDelegate.tableView(_:didSelectRowAtIndexPath:)))
  .subscribeNext { (parameters) in
		// User did select cell at index path
  }

Playing 🏈

with Observables

fill


Observable

let tracksFetcher = api.fetchTracks // Background
	.asObservable()

Error handling

let tracksFetcher = api.fetchTracks // Background
	.asObservable()
	.retry(3)
	.catchErrorJustReturn([])

Mapping

let tracksFetcher = api.fetchTracks // Background
	.asObservable()
	.retry(3)
	.catchErrorJustReturn([])
	.map(TrackEntity.mapper().map)

Filtering

let tracksFetcher = api.fetchTracks // Background
	.asObservable()
	.retry(3)
	.catchErrorJustReturn([])
	.map(TrackEntity.mapper().map)
	.filter { $0.name.contains(query) }

Flatmapping

let tracksFetcher = api.fetchTracks // Background
	.asObservable()
	.retry(3)
	.catchErrorJustReturn([])
	.map(TrackEntity.mapper().map)
	.filter { $0.name.contains(query) }
	.flatMap { self.rx_trackImage(track: $0) }

Observation thread

let tracksFetcher = api.fetchTracks // Background
	.asObservable()
	.retry(3)
	.catchErrorJustReturn([])
	.map(TrackEntity.mapper().map)
	.filter { $0.name.contains(query) }
	.flatMap { self.rx_trackImage(track: $0) }
	.observeOn(MainScheduler.instance) // Main thread

Throttling

Tipical reactive example

func tracksFetcher(query: String) -> Observable<[TrackEntity]>

searchTextField
	.rx_text.throttle(0.5, scheduler: MainScheduler.instance)
	.flatmap(tracksFetcher)
	.subscribeNext { tracks in
		// Yai! Tracks searched
	}

Other operators

Combining / Skipping Values / Deferring / Concatenation / Deferring / Take some values / Zipping

Ease plugging observables


Observing πŸ€“

events


Subscribing

observable
  subscribe { event
		case .Next(let value):
			print(value)
		case .Completed:
			print("completed")
		case .Error(let error):
			print("Error: \(error)")
	}

Bind changes over the time to an Observable


observable β–Ί bind β–Ί observer

(observer subscribes to observable events)


observable // Observable<String>
	.bindTo(field.rx_text)

rx_text? πŸ˜•


Reactive Property

(Control Property in RxSwift)


Subject

Observer & Observable

(Yes.. both)

Reactive Variable

(Variable in RxSwift)

(Property in ReactiveCocoa)


let text: Variable<String> = Variable("")
text.asObservable()
	.subscribeNext { newText
		print("The text did change. New text: \(newText)")
	}

😩 Caveats

Because yes...

it couldn't be perfect



fill


Debugging

self.createComment(issueId: issueId, body: body).observable()
    .observeOn(MainScheduler.instance)
    .doOn(onError: { [weak self] error in
        // show Error
    })
    .subscribeCompleted { [weak self] in
        // show Success
    }
    .addDisposableTo(self.disposeBag)

// RXSWIFT: operador para debuggear -> muestra traza


Retain cycles

class IssuePresenter {
  let commandFactory = CommandFactory()

  func fetch() {
     commandFactory.fetchCommand.observable()
     .bindTo { self.something }
     .addDisposableTo(disposeBag)
  }
}

Retain cycles

class IssuePresenter {
  let commandFactory = CommandFactory()

  func fetch() {
     commandFactory.fetchCommand.observable()
     .bindTo { self.something }
     .addDisposableTo(disposeBag)
  }
}

presenter -> commandFactory -> fetchCommand -> presenter

Retain cycles

class IssuePresenter {
  let commandFactory = CommandFactory()

  func fetch() {
     commandFactory.fetchCommand.observable()
     .bindTo { [weak self] in self?.something }
     .addDisposableTo(disposeBag)
  }
}

presenter -> commandFactory -> fetchCommand -> presenter

Unsubscription

// Disposable bag


Threading?

let todo: String = "Add code example"

A great power comes with a great responsibility

fill

don't remember what is this about xD



Conclusions


Prevents stateful code


Aims unidirectional data flow


Data flow manipulation becomes easier


But... πŸ˜“


You couple your project to a library πŸ‘«


Reactive code spreads like a virus πŸ‘½

Overreactive


Define reactive design guidelines and stick to them


Have Reactive fun πŸŽ‰


References

rxmarbles.cmo http://community.rxswift.org github.com/rxswift/rxswift github.com/reactivecocoa/reactivecocoa


We are hiring

❄️ BERLIN - BARCELONA 🌴

fill


fill