Top Banner
UNIDIRECTIONAL DATA FLOW WITH REACTOR
41

Unidirectional Data Flow with Reactor

Apr 16, 2017

Download

Software

Jason Larsen
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Unidirectional Data Flow with Reactor

UNIDIRECTIONAL DATA FLOWWITH REACTOR

Page 2: Unidirectional Data Flow with Reactor

WHAT IS THE STATE OF YOUR APP?

Page 3: Unidirectional Data Flow with Reactor

WHERE DOES STATE LIVE?

Page 4: Unidirectional Data Flow with Reactor

WHAT DOES STATE LOOK LIKE?

Page 5: Unidirectional Data Flow with Reactor

STATEstruct Player: State { var name: String var level: Int}

Page 6: Unidirectional Data Flow with Reactor

STATE COMPOSITIONstruct RPGState { var players: Player var monsters: Monsters

// ...}

Page 7: Unidirectional Data Flow with Reactor

CHANGING STATEDATA FLOW

Page 8: Unidirectional Data Flow with Reactor

▸ Delegates▸ Target-Actions▸ Completion Blocks▸ Notification Center

▸ KVO▸ Segues▸ didSet

▸ NSFetchedResultsController

Page 9: Unidirectional Data Flow with Reactor

WHAT DIRECTION IS THE DATA FLOWING?

Page 10: Unidirectional Data Flow with Reactor

SPOT THE BUG!class ViewController: UIViewController { @IBOutlet var titleLabel: UILabel!

var name: String? { didSet { titleLabel.text = name } }}

Page 11: Unidirectional Data Flow with Reactor

SPOT THE NEW BUG!class ViewController: UIViewController { @IBOutlet var titleLabel: UILabel?

var name: String? { didSet { titleLabel?.text = name } }}

Page 12: Unidirectional Data Flow with Reactor

THE IMPLICITLY UNWRAPPED OPTIONAL DANCEclass ViewController: UIViewController { @IBOutlet var titleLabel: UILabel?

var name: String? { didSet { configureView() } }

func configureView() { titleLabel?.text = name }

override func viewDidLoad() { super.viewDidLoad() configure() }}

Page 13: Unidirectional Data Flow with Reactor

PROBLEM: UNDEFINED DATA

FLOW

Page 14: Unidirectional Data Flow with Reactor

SINGLE SOURCE OF TRUTH™

Page 15: Unidirectional Data Flow with Reactor

(STATE) → VIEWUI AS A PURE FUNCTION OF STATE

Page 16: Unidirectional Data Flow with Reactor

REDRAW VIEW ON EVERY STATE CHANGE?!?!?

Page 17: Unidirectional Data Flow with Reactor

REACT✨ VIRTUAL DOM ✨

Page 18: Unidirectional Data Flow with Reactor

!UIKIT

Page 19: Unidirectional Data Flow with Reactor

(STATE) → VOIDWITH SIDE EFFECTS

Page 20: Unidirectional Data Flow with Reactor

extension ViewController { func update(with state: State) { nameLabel.text = state.name }}

Page 21: Unidirectional Data Flow with Reactor

UNIDIRECTIONAL DATA FLOWALL UPDATES FLOW IN SAME DIRECTION

Page 22: Unidirectional Data Flow with Reactor

JARSEN/REACTOR

Page 23: Unidirectional Data Flow with Reactor
Page 24: Unidirectional Data Flow with Reactor

CORE▸ Holds the Single Source of Truth™▸ Only the Core can change the state

▸ Notifies all subscribers with state changes

Page 25: Unidirectional Data Flow with Reactor

OBSERVING STATEclass ViewController: core.Subscriber { var core = App.sharedCore @IBOutlet var nameLabel: UILabel!

override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) core.add(subscriber: self) }

override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) core.remove(subscriber: self) }

func update(with state: State) { nameLabel.text = state.name }}

Page 26: Unidirectional Data Flow with Reactor

UPDATING STATEstruct Increment: Event {}

extension ViewController { @IBAction func didPressIncrement() { core.fire(event: Increment()) }}

Page 27: Unidirectional Data Flow with Reactor

ASYNC EVENTSstruct Update<T>: Event { var value: T}

struct GetUsers: Command { func execute(state: State, core: Core<State>) { myNetworkThing.get("users") { json in // transform to user object, using Marshal, of course core.fire(event: Update(value: users)) } }}

Page 28: Unidirectional Data Flow with Reactor

ASYNC EVENTSextension ViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) core.add(subscriber: self) core.fire(command: GetUsers()) }}

Page 29: Unidirectional Data Flow with Reactor

HOW DO EVENTS UPDATE STATE?struct Player: State { var name: String var level: Int

mutating func react(to event: Event) { switch event { case let _ as LevelUp: level += 1 default: break } }}

Page 30: Unidirectional Data Flow with Reactor

HOW DO EVENTS UPDATE COMPOSED STATES?struct RPGState: State { var player: Player var monsters: Monsters

mutating func react(to event: Event) { player.react(to: event) monsters.react(to: event) }}

Page 31: Unidirectional Data Flow with Reactor

MIDDLEWAREGood for side effects like logging, analytics, displaying errors...

struct LoggingMiddleware: Middleware { func process(event: Event, state: State) { switch event { case _ as LevelUp: print("Leveled Up!") default: break } }}

Page 32: Unidirectional Data Flow with Reactor

HOT RELOADINGUSING MARSHAL + KZFILEWATCHER

Page 33: Unidirectional Data Flow with Reactor
Page 34: Unidirectional Data Flow with Reactor

OPTIMISTIC NETWORK RESULTS

Page 35: Unidirectional Data Flow with Reactor

TIME TRAVEL

Page 36: Unidirectional Data Flow with Reactor

STATE RESTORATION

Page 37: Unidirectional Data Flow with Reactor

PERFORMANCE CONCERNS

Page 38: Unidirectional Data Flow with Reactor

Don't like the API? ☹Don't like 3rd Party? "

ROLL YOUR OWN !It's a straightforward, not-too-novel pattern.

Page 39: Unidirectional Data Flow with Reactor

NAVIGATION STATE / ROUTING