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

Lessons learned from a shared code base

Lessons learned from a shared code base

We experimented with Kotlin Multiplatform by creating a tvOS and Android TV app. Here are our learnings.

Videos of the prototypes can be found here:
- tvOS: https://youtu.be/EAXAxgsx_AI
- Android TV: https://youtu.be/gK7xERZoUu0

Hannes Dorfmann

February 11, 2020
Tweet

More Decks by Hannes Dorfmann

Other Decks in Programming

Transcript

  1. • Existing Cross Platform solutions • Kotlin Native • Future

    • Q&A • Demo CONFIDENTIAL FREELETICS GMBH Agenda
  2. Presentation Layer Application Layer Data Access Layer View ViewModel Business

    Rules Navigation Interactor Use Case Repository DAO DB - HTTP - FS Layered Architecture
  3. Presentation Layer Application Layer Data Access Layer View ViewModel Business

    Rules Navigation Interactor Use Case Repository DAO DB - HTTP - FS Web HTML + CSS React Redux HTTP GraphQL Web Standards like JS
  4. Presentation Layer Application Layer Data Access Layer View ViewModel Business

    Rules Navigation Interactor Use Case Repository DAO DB - HTTP - FS Web / PWA HTML + CSS React Redux HTTP GraphQL Service Worker Web Standards like JS
  5. Presentation Layer Application Layer Data Access Layer View ViewModel Business

    Rules Navigation Interactor Use Case Repository DAO DB - HTTP - FS Web Standards like JS - kind of Ionic / Cordova HTML + CSS React UI Components Redux OS Bridge i.e. Camera HTTP native
  6. Presentation Layer Application Layer Data Access Layer View ViewModel Business

    Rules Navigation Interactor Use Case Repository DAO DB - HTTP - FS Java Script React Native Native UI React Redux OS Bridge i.e. Camera HTTP native native
  7. Presentation Layer Application Layer Data Access Layer View ViewModel Business

    Rules Navigation Interactor Use Case Repository DAO DB - HTTP - FS C# (compiles arm, Runtime) Xamarin Xamarin Forms ViewModel BLoC Redux OS Bridge i.e. Camera HTTP native Native UI native
  8. Presentation Layer Application Layer Data Access Layer View ViewModel Business

    Rules Navigation Interactor Use Case Repository DAO DB - HTTP - FS Dart (compiles native arm) Flutter UI Drawing Components BLoC Redux OS Bridge i.e. Camera HTTP native
  9. Presentation Layer Application Layer Data Access Layer View ViewModel Business

    Rules Navigation Interactor Use Case Repository DAO DB - HTTP - FS C++ / JNI C++ / Rust / Swift Logic OS HTTP native View native ViewModel Navigation Business
  10. Presentation Layer Application Layer Data Access Layer View ViewModel Business

    Rules Navigation Interactor Use Case Repository DAO DB - HTTP - FS Kotlin (JVM, ARM) Kotlin Native Business Logic OS HTTP native View native ViewModel Navigation
  11. Business Logic OS HTTP View ViewModel Navigation Logic OS HTTP

    View ViewModel Navigation Business C++ / Rust Kotlin Native Native UI React Xamarin Forms Redux OS Bridge i.e. Camera HTTP Xamarin React Native UI Drawing Components BLoC Redux OS Bridge i.e. Camera HTTP Flutter HTML React Redux HTTP GraphQL Ionic Web OS Bridge
  12. CONFIDENTIAL FREELETICS GMBH Expect / Actual In CommonMain In AndroidMain

    In TvOSMain expect fun log(tag: String, message: String) import android.util.Log actual fun log(tag: String, message: String) { Log.d(tag, message) } import platform.Foundation.NSLog actual fun log(tag: String, message: String) { NSLog("${tag}: ${message}") }
  13. Use an architecture that allows to share as much code

    as possible! CONFIDENTIAL FREELETICS GMBH Main goals
  14. CONFIDENTIAL FREELETICS GMBH Workout StateMachine inState<CountdownState> { observeWhileInState(countdownStopWatch.timeElapsed()) { _,

    _, setState -> setState { state -> val timeLeft = state.timeLeft - 1 if (timeLeft >= 0) { CountdownState(timeLeft) } else { createFirstExerciseState(rounds) } } } } data class CountdownState( val timeLeft: Int )
  15. CONFIDENTIAL FREELETICS GMBH Workout StateMachine inState<ExerciseState> { on<NextExerciseAction> { action,

    getState, setState -> setState { state -> createNextExerciseState(state, rounds) } } } data class ExerciseState( override var secondsElapsed: Long = 0, override var currentRoundIndex: Int = 0, override var currentExerciseIndex: Int = 0, override var workoutProgress: Int = 0, override var nextExercise: RoundExercise? )
  16. CONFIDENTIAL FREELETICS GMBH Workout StateMachine class MyStateMachine ( initialState: MyState

    ): FlowReduxStateMachine<MyState, MyAction>(initialState) { inState<StateOne> { on<ActionOne> { action, getState, setState -> setState { //create new state } } //on {...} } inState<StateTwo> { observeWhileInState(someFlowWhichEmitsValues) { value, getState, setState -> setState { //create new state } } //on {...} } }
  17. CONFIDENTIAL FREELETICS GMBH Testing the StateMachine Given("a workout with a

    single round") { //initialize StateMachine and required data here val state = stateMachine.state.record() And("the user wants to do the workout") { When("it starts") { Then("it starts with a countdown and 5 seconds left") { state shouldEmitNext WorkoutState.CountdownState(5) } } for (i in 4 downTo 0) { When("One countdown second elapsed") { countdown.nextTick() Then("Time left in CountdownState is $i") { state shouldEmitNext WorkoutState.CountdownState(i) } } } //When(...) When("Next Button clicked") { stateMachine.nextExercise() Then("next exercise state is emitted") { state shouldEmitNext RepExerciseState(...) } } }
  18. Use an architecture that allows to share as much code

    as possible! How did we achieve that goal? - Fragment / ViewController -> ViewModel -> StateMachine -> Repository -> DB/API - Make it easy to render the UI! (By using a state machine which is easy to read and test) CONFIDENTIAL FREELETICS GMBH Main goals
  19. CONFIDENTIAL FREELETICS GMBH How much code did I share? Android

    Code: ~2500 LOC TvOS Code: ~1400 LOC Shared Code: ~4600 LOC
  20. - TvOS wasn’t even supported when we started - Libraries

    had a hard time catching up to changes - Coroutines can only be used in Kotlin - Features have to be possible on both platforms (e.g. background services) - There is not always an answer for your problem CONFIDENTIAL FREELETICS GMBH Challenges
  21. CONFIDENTIAL FREELETICS GMBH class WorkoutScreen extends React.Component { render() {

    if (this.state instanceof CountdownState) return <Countdown timeLeft={this.state.timeLeft} />; else if (this.state instanceof ExerciseState) return <Exercise exercise={this.state} />; } }
  22. CONFIDENTIAL FREELETICS GMBH class WorkoutScreen : View { @State var

    state: State var body: some View { switch(state){ case let countdown as CountdownState: Countdown(timeLeft: countdown.timeLeft) case let exercise as ExerciseState: Exercise(exercise: exercise) } } }
  23. CONFIDENTIAL FREELETICS GMBH @Composable fun WorkoutScreen(state : State) { when(state){

    is CountdownState -> Countdown(state.timeLeft) is ExcerciseState -> Exercise(state) } }
  24. Q&A