Borrowing the Best of Web to Make Native Better, with Christina Lee & Brandon Kase

1fa9cb8c7997c8c4d3d251fb5e41f749?s=47 Realm
March 31, 2016

Borrowing the Best of Web to Make Native Better, with Christina Lee & Brandon Kase

1fa9cb8c7997c8c4d3d251fb5e41f749?s=128

Realm

March 31, 2016
Tweet

Transcript

  1. Borrowing the best of the web to make native better

    Brandon Kase & Christina Lee
  2. Why are we here? Let me tell you a story!

    about a button a complicated button
  3. Take 1: boss asks for friend button we say, 'OK!

    We'll do it this week!' we do not finish it in a week woah...this is very complicated! boss is less than thrilled
  4. Take 2: boss asks for friend button we say, 'OK!

    We'll do it this week!' we modify our approach we do not finish it in a week we finish it in a day! boss is thrilled Brandon and Christina still have jobs, hurray!
  5. What went wrong Fetch data before view transitions Optimistically update

    components Send server requests and react to responses
  6. Side Effects Touch global state, make network requests Side-effects are

    bad, but necessary
  7. Most Common Pitfalls Mutability Asynchronicity

  8. As developers, we are expected to handle optimistic updates, server-

    side rendering, fetching data before performing route transitions, and so on... This complexity is difficult to handle as we’re mixing two concepts that are very hard for the human mind to reason about: mutation and asynchronicity. I call them Mentos and Coke” Motivation | Redux
  9. None
  10. Luckily, we are not alone

  11. Web Allies The web faces all of these challenges and

    more Unlike native; however, it's iteration rate is fast Not "one" UI framework
  12. So what did the web come up with?

  13. Ways to manage side-effects View State

  14. Ways to manage side-effects Separation of concerns Mutability and asynchronicity

    are decoupled
  15. None
  16. Flux

  17. Redux / / T h e c u r r

    e n t a p p l i c a t i o n s t a t e ( l i s t o f t o d o s a n d c h o s e n f i l t e r ) l e t p r e v i o u s S t a t e = { v i s i b l e T o d o F i l t e r : ' S H O W _ A L L ' , t o d o s : [ { t e x t : ' R e a d t h e d o c s . ' , c o m p l e t e : f a l s e } ] } / / T h e a c t i o n b e i n g p e r f o r m e d ( a d d i n g a t o d o ) l e t a c t i o n = { t y p e : ' A D D _ T O D O ' , t e x t : ' U n d e r s t a n d t h e f l o w . ' } / / Y o u r r e d u c e r r e t u r n s t h e n e x t a p p l i c a t i o n s t a t e l e t n e x t S t a t e = t o d o A p p ( p r e v i o u s S t a t e , a c t i o n ) -- Dan Abaramov's Data Flow example in Redux
  18. Cycle.js

  19. Cycle.js inputs = read effects you care about outputs =

    write effects you want performed all application logic is pure!
  20. Cycle.js

  21. from cycle.js.org

  22. Common Traits Unidirectional and circular data flows Separation of concerns

  23. How do we benefit on Android?

  24. Logic is made easy Implicit data flow of your app

    becomes explicit. Immutable views of a mutable world Debugging is made easy All edge cases caught at compile-time. Single source of truth. Time Travel
  25. How can you adopt this View: React native Anvil State:

    Direct port of Redux/Cycle/etc.
  26. We focused on State Don't have to fight Android's UI

    framework Easy to introduce
  27. None
  28. Native (kotlin) Single-Atom-State (like redux) Pure Functional Reactive (like cycle)

    Composable (like cycle)
  29. Aside: RxJava

  30. Aside: RxJava Reactive programming is programming with asynchronous data streams

    Andre Staltz Global event emitter << Event buses << Data stream
  31. Aside: RxJava When we say streams, we mean push-based event

    streams, not pull-based infinite list streams
  32. Aside: RxJava What can this look like in practice? Streams

    of button taps Streams of snapshots of changing data Streams from network responses
  33. Aside: RxJava RxJava Reactive programming with streams Tools to combine

    and transform those streams
  34. Aside: RxJava from RxMarbles

  35. Aside: RxJava from RxMarbles

  36. Aside: RxJava O b s e r v a b

    l e . j u s t ( 1 , 2 , 3 )
  37. Example

  38. Example 0:09 0:14

  39. Not just a counter

  40. None
  41. 1. View-Model State d a t a c l a

    s s / * V i e w - M o d e l * / S t a t e ( v a l n u m L i k e s : I n t , v a l n u m C o m m e n t s : I n t , v a l s h o w N e w H i g h l i g h t : B o o l e a n , v a l i m g U r l : S t r i n g ? , v a l s h o w U n d o : B o o l e a n )
  42. None
  43. 2. View Intentions / / M o d e i

    s e i t h e r t a p p e d o r u n t a p p e d d a t a c l a s s V i e w I n t e n t i o n s ( v a l p h o t o s : O b s e r v a b l e < P h o t o > , v a l m o d e s : O b s e r v a b l e < M o d e . S u m > , v a l g l o b a l R e a d T s : O b s e r v a b l e < L o n g > )
  44. None
  45. 3. Model State / / M o d e i

    s e i t h e r t a p p e d o r u n t a p p e d d a t a c l a s s / * M o d e l * / S t a t e ( v a l p h o t o : P h o t o ? , v a l i s N e w : B o o l e a n , v a l m o d e : M o d e . S u m , ) : R a m S t a t e < . . . > v a l i n i t i a l S t a t e = S t a t e ( p h o t o = n u l l , i s N e w = f a l s e , m o d e = M o d e . u n t a p p e d )
  46. 4. View Intentions => Model State Changes State changes? We

    want functional code. We want immutability. Think of a state change as a function f u n c c h a n g e ( c u r r e n t S t a t e : S t a t e ) - > S t a t e / * n e x t S t a t e * /
  47. 4. View Intentions => Model State Changes v a l

    m o d e l : ( V i e w I n t e n t i o n s ) - > O b s e r v a b l e < ( S t a t e ) - > S t a t e > = { i n t e n t i o n s - > v a l m o d e C h a n g e s : O b s e r v a b l e < ( S t a t e ) - > S t a t e > = i n t e n t i o n s . m o d e s . m a p { / * . . . * / } v a l p h o t o C h a n g e s : O b s e r v a b l e < ( S t a t e ) - > S t a t e > = i n t e n t i o n s . p h o t o s . m a p { / * . . . * / } v a l t s C h a n g e s : O b s e r v a b l e < ( S t a t e ) - > S t a t e > = i n t e n t i o n s . g l o b a l R e a d T s . m a p { / * . . . * / } O b s e r v a b l e . m e r g e ( m o d e C h a n g e s , p h o t o C h a n g e s , t s C h a n g e s ) }
  48. 4. View Intentions => Model State Changes v a l

    m o d e C h a n g e s : O b s e r v a b l e < ( S t a t e ) - > S t a t e > = i n t e n t i o n s . m o d e s . m a p { m o d e - > { s t a t e : S t a t e - > S t a t e ( s t a t e . p h o t o , s t a t e . i s N e w , m o d e ) } }
  49. None
  50. 5. Model State => View-Model State v a l v

    i e w M o d e l : ( O b s e r v a b l e < M o d e l . S t a t e > ) - > O b s e r v a b l e < V i e w M o d e l . S t a t e > = { s t a t e S t r e a m - > s t a t e S t r e a m . m a p { s t a t e - > v a l u n d o a b l e = s t a t e . m o d e = = M o d e . t a p p e d v a l l i k e s = s t a t e . p h o t o ? . l i k e _ d e t a i l s ? : e m p t y L i s t ( ) v a l c o m m e n t s = s t a t e . p h o t o ? . c o m m e n t s ? : e m p t y L i s t ( ) V i e w M o d e l . S t a t e ( n u m L i k e s = l i k e s . s u m B y { i t . m u l t i p l i e r } , n u m C o m m e n t s = c o m m e n t s . c o u n t , s h o w N e w H i g h l i g h t = s t a t e . i s N e w , i m g U r l = / * . . . * / , s h o w U n d o = / * . . . * / ) } }
  51. None
  52. 6. View-Model => Mutate the View c l a s

    s P h o t o C o m p o n e n t ( v i e w I n t e n t i o n s : V i e w I n t e n t i o n s , v i e w : P h o t o C e l l V i e w ) : S t a r t S t o p C o m p o n e n t b y C o m p o n e n t ( d r i v e r = / * . . . * / , m o d e l = / * . . . * / )
  53. 6. View-Model => Mutate the View d r i v

    e r = V i e w D r i v e r < V i e w I n t e n t i o n s , V i e w M o d e l . S t a t e > ( i n t e n t i o n = v i e w I n t e n t i o n s , o n V i e w S t a t e = { o l d , s t a t e - > i f ( o l d ? . i m g U r l ! = s t a t e . i m g U r l ) { v i e w . s e t I m g ( s t a t e . i m g U r l ) } / * . . . * / } ) ,
  54. 6. View-Model => Mutate the View m o d e

    l = V i e w D r i v e r . m a k e M o d e l ( i n i t i a l S t a t e = M o d e l . i n i t i a l S t a t e , c r e a t e S t a t e = M o d e l . c r e a t e S t a t e , m o d e l = M o d e l . m o d e l , v i e w M o d e l = V i e w M o d e l . v i e w M o d e l )
  55. Stick it in a recycler-view, hook up the side-effects into

    view intentions and you're done
  56. None
  57. ViewIntentions The inputs to your component The photo, the mode,

    the tap timestamp Model Transform the inputs into state changes Change mode, change isNew, change photo ViewModel Transform model state to view-model state Extract photo url, like counts, etc Component Apply mutations to your view based on your view-model Use the View-Model to change the underlying Android view
  58. Under the hood Enforce viewintentions/model/view-model structure RxJava does heavy-lifting and

    a magic scan
  59. Under the hood

  60. Implementation of Redux in one-line m o d e l

    S t r e a m . s c a n ( i n i t i a l S t a t e , { c u r r e n t S t a t e , t r a n s f o r m - > t r a n s f o r m ( c u r r e n t S t a t e ) } )
  61. Bonus Cycle.js-like side-effect drivers Configurable model state persistance within state

    Auto-start and stop components onPause/onResume
  62. Just like cycle read effects are inputs write effects are

    outputs Effects are decoupled from business logic
  63. What does it look like in production?

  64. Results: The Good It wouldn't compile

  65. Results: The Good When it did compile, it worked!

  66. Results: The Good Incredibly modular and composable LEGO-like plug-and-play

  67. Results: The Good Easy to test (by hand + by

    unit test) & debug REALLY EASY Mocking inputs is trivial UI component is defined ONLY by it's state
  68. Results: The Good Easy to maintain Spec change? (possibly) add

    an input stream add another m a p in the model
  69. Results: The Bad Ramp up necessary

  70. Results: The Bad Animations are hard chase or interpolate underlying

    state? probably additive animations (google it)
  71. Results: The Bad? Boiler plate (screenshot of files) Always the

    4 pieces c y k l i c repo has counter example in one file
  72. Results: The Surprising It's actually not slow No noticeable perf

    hit
  73. Results: The Conclusion

  74. Results: The Conclusion We have more powerful tools now (i.e.

    Kotlin + Functional programming) Let's use them Question everything
  75. Cyklic Github: bkase/cyklic

  76. Thanks Brandon Kase Christina Lee bkase@highlig.ht christina@highlig.ht @bkase_ @runchristinarun bkase.com

    Github: bkase/cyklic