Upgrade to Pro — share decks privately, control downloads, hide ads and more …

A portal from Elm to Swift

A portal from Elm to Swift

An introduction to the Elm architecture analyzing what's wrong with all the architectures that came before (MVC, MVP, MVVM) and how to implement it in Swift.

Guido Marucci Blas

April 26, 2017
Tweet

More Decks by Guido Marucci Blas

Other Decks in Programming

Transcript

  1. A PORTAL FROM ELM TO SWIFT 1 — A Portal

    from Elm to Swift by Guido Marucci Blas
  2. GUIDO MARUCCI BLAS CO-FOUNDER & TECH LEAD AT WOLOX CTO

    AT SYRMO Twitter: @guidomb Blog: http://guidomb.blog GitHub: http://github.com/guidomb 2 — A Portal from Elm to Swift by Guido Marucci Blas
  3. A PORTAL FROM ELM TO SWIFT 5 — A Portal

    from Elm to Swift by Guido Marucci Blas
  4. GO BACK IN TIME ... 10 — A Portal from

    Elm to Swift by Guido Marucci Blas
  5. MVC - PROBLEMS > Massive fat controllers > Hard to

    test > Highly coupled > Lots of boilerplate 13 — A Portal from Elm to Swift by Guido Marucci Blas
  6. MVC - POSSIBLE SOLUTIONS > Extract business logic > Extract

    "side-effects" into services with protocols > Use dependency injection > Use notifications 14 — A Portal from Elm to Swift by Guido Marucci Blas
  7. MVP - PROBLEMS > Way more boilerplate > Most of

    the problems / solutions from MVC still apply. 17 — A Portal from Elm to Swift by Guido Marucci Blas
  8. MVVM - PROBLEMS > Requires a binding mechanisim > It's

    not clear who is responsable for view transitions > Forces you to use DI framework, singletons or propagate dependencies over the whole hierarchy 21 — A Portal from Elm to Swift by Guido Marucci Blas
  9. All this architectures still have the same problems 25 —

    A Portal from Elm to Swift by Guido Marucci Blas
  10. The application's state is spread over several components 26 —

    A Portal from Elm to Swift by Guido Marucci Blas
  11. Which means state is mutated all over the place 27

    — A Portal from Elm to Swift by Guido Marucci Blas
  12. Which also means that if state needs to be shared,

    it must be kept in sync 28 — A Portal from Elm to Swift by Guido Marucci Blas
  13. Shared mutable state is the root of all EVIL 29

    — A Portal from Elm to Swift by Guido Marucci Blas
  14. yes, there is more 31 — A Portal from Elm

    to Swift by Guido Marucci Blas
  15. The other big issue is how this architectures manage side-

    effects 32 — A Portal from Elm to Swift by Guido Marucci Blas
  16. What do we do next? 33 — A Portal from

    Elm to Swift by Guido Marucci Blas
  17. but what about Swift? 37 — A Portal from Elm

    to Swift by Guido Marucci Blas
  18. If you want the full power of the Elm architecture

    you need ... 39 — A Portal from Elm to Swift by Guido Marucci Blas
  19. But UIKit will make your life miserable 41 — A

    Portal from Elm to Swift by Guido Marucci Blas
  20. A (potentially) cross-platform, declarative and immutable Swift library for building

    user interfaces 43 — A Portal from Elm to Swift by Guido Marucci Blas
  21. 100% written in Swift 44 — A Portal from Elm

    to Swift by Guido Marucci Blas
  22. The declarative API is NOT coupled to UIKit 45 —

    A Portal from Elm to Swift by Guido Marucci Blas
  23. enum Message { case like case goToDetailScreen } let component:

    Component<Message> = container( children: [ label( text: "Hello PortalView!", style: labelStyleSheet() { base, label in base.backgroundColor = .white label.textColor = .red label.textSize = 12 }, layout: layout() { $0.flex = flex() { $0.grow = .one } $0.justifyContent = .flexEnd } ) button( properties: properties() { $0.text = "Tap to like!" $0.onTap = .like } ) button( properties: properties() { $0.text = "Tap to got to detail screen" $0.onTap = .goToDetailScreen } ) ] ) 47 — A Portal from Elm to Swift by Guido Marucci Blas
  24. label( text: "Hello PortalView!", style: labelStyleSheet() { base, label in

    base.backgroundColor = .white label.textColor = .red label.textSize = 12 }, layout: layout() { $0.flex = flex() { $0.grow = .one } $0.justifyContent = .flexEnd } ) 48 — A Portal from Elm to Swift by Guido Marucci Blas
  25. button( properties: properties() { $0.text = "Tap to like!" $0.onTap

    = .like } ) 49 — A Portal from Elm to Swift by Guido Marucci Blas
  26. public indirect enum Component<MessageType> { case button(ButtonProperties<MessageType>, StyleSheet<ButtonStyleSheet>, Layout) case

    label(LabelProperties, StyleSheet<LabelStyleSheet>, Layout) case mapView(MapProperties, StyleSheet<EmptyStyleSheet>, Layout) case imageView(Image, StyleSheet<EmptyStyleSheet>, Layout) case container([Component<MessageType>], StyleSheet<EmptyStyleSheet>, Layout) case table(TableProperties<MessageType>, StyleSheet<TableStyleSheet>, Layout) case collection(CollectionProperties<MessageType>, StyleSheet<EmptyStyleSheet>, Layout) case carousel(CarouselProperties<MessageType>, StyleSheet<EmptyStyleSheet>, Layout) case touchable(gesture: Gesture<MessageType>, child: Component<MessageType>) case segmented(ZipList<SegmentProperties<MessageType>>, StyleSheet<SegmentedStyleSheet>, Layout) case progress(ProgressCounter, StyleSheet<ProgressStyleSheet>, Layout) case textField(TextFieldProperties<MessageType>, StyleSheet<TextFieldStyleSheet>, Layout) case custom(componentIdentifier: String, layout: Layout) case spinner(Bool, StyleSheet<SpinnerStyleSheet>, Layout) } 50 — A Portal from Elm to Swift by Guido Marucci Blas
  27. but what about state management and side-effects? 52 — A

    Portal from Elm to Swift by Guido Marucci Blas
  28. public protocol Application { associatedtype MessageType associatedtype StateType associatedtype CommandType

    associatedtype RouteType: Route associatedtype SubscriptionType: Equatable associatedtype NavigatorType: Navigator var initialState: StateType { get } var initialRoute: RouteType { get } func translateRouteChange(from currentRoute: RouteType, to nextRoute: RouteType) -> MessageType? func update(state: StateType, message: MessageType) -> (StateType, CommandType?)? func view(for state: StateType) -> View<RouteType, MessageType, NavigatorType> func subscriptions(for state: StateType) -> [Subscription<MessageType, RouteType, SubscriptionType>] } 54 — A Portal from Elm to Swift by Guido Marucci Blas
  29. public struct View<RouteType: Route, MessageType, NavigatorType: Navigator> { public typealias

    ActionType = Action<RouteType, MessageType> public enum Content { case alert(properties: AlertProperties<ActionType>) case component(Component<ActionType>) } public let navigator: NavigatorType public let root: RootComponent<ActionType> public let content: Content } 55 — A Portal from Elm to Swift by Guido Marucci Blas
  30. public indirect enum Action<RouteType: Route, MessageType> { case dismissNavigator(thenSend: Action<RouteType,

    MessageType>?) case navigateToPreviousRoute(preformTransition: Bool) case navigate(to: RouteType) case sendMessage(MessageType) } 56 — A Portal from Elm to Swift by Guido Marucci Blas
  31. public protocol Route: Equatable { var previous: Self? { get

    } } public protocol Navigator: Equatable { associatedtype RouteType: Route var baseRoute: RouteType { get } } 57 — A Portal from Elm to Swift by Guido Marucci Blas
  32. public protocol CommandExecutor { associatedtype CommandType associatedtype MessageType func execute(command:

    CommandType, dispatch: @escaping (MessageType) -> Void) } 58 — A Portal from Elm to Swift by Guido Marucci Blas
  33. import UIKit import PortalApplication import PortalView let context = UIKitApplicationContext(

    application: Voices(), commandExecutor: VoicesCommandExecutor(), subscriptionManager: VoicesSubscriptionManager(), customComponentRenderer: VoidCustomComponentRenderer() ) context.registerMiddleware(TimeLogger()) PortalUIApplication.start(applicationContext: context) { message in switch message { case .didFinishLaunching(_, _): return .applicationLaunched default: return .none } } 59 — A Portal from Elm to Swift by Guido Marucci Blas
  34. PORTAL - WARNING > Still very early stage > API

    will probably change > Still needs important features like UI diffing 62 — A Portal from Elm to Swift by Guido Marucci Blas
  35. PORTAL - MILESTONES > API stability > Improve performance >

    Cool tooling, like hot UI reload > Server side rendering? > Android support 63 — A Portal from Elm to Swift by Guido Marucci Blas
  36. you are crazy, I cannot do a full rewrite of

    my massive application ... 64 — A Portal from Elm to Swift by Guido Marucci Blas
  37. you can add Portal gradually into your application or ...

    65 — A Portal from Elm to Swift by Guido Marucci Blas
  38. Take the key concepts from this talk and apply them

    to your existing architecture 66 — A Portal from Elm to Swift by Guido Marucci Blas
  39. TL; DR; > Decouple side-effects froms logic by describing effects

    using values > Avoid state sincronization, have a central place where state is mutated > Avoid deep view / object hierarchy, prefer wide hierarchies 67 — A Portal from Elm to Swift by Guido Marucci Blas