Slide 1

Slide 1 text

CONFIDENTIAL FREELETICS GMBH Lessons Learned from a Shared Codebase

Slide 2

Slide 2 text

● Existing Cross Platform solutions ● Kotlin Native ● Future ● Q&A ● Demo CONFIDENTIAL FREELETICS GMBH Agenda

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

Why do I talk about Kotlin Native? CONFIDENTIAL FREELETICS GMBH

Slide 14

Slide 14 text

So, what is Kotlin Native? CONFIDENTIAL FREELETICS GMBH

Slide 15

Slide 15 text

How to build a Kotlin Native App? CONFIDENTIAL FREELETICS GMBH

Slide 16

Slide 16 text

CONFIDENTIAL FREELETICS GMBH Project Structure

Slide 17

Slide 17 text

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}") }

Slide 18

Slide 18 text

FreeleticsTV How Freeletics would look like on a TV... CONFIDENTIAL FREELETICS GMBH

Slide 19

Slide 19 text

CONFIDENTIAL FREELETICS GMBH tvOS https://youtu.be/EAXAxgsx_AI

Slide 20

Slide 20 text

CONFIDENTIAL FREELETICS GMBH AndroidTV https://youtu.be/gK7xERZoUu0

Slide 21

Slide 21 text

Use an architecture that allows to share as much code as possible! CONFIDENTIAL FREELETICS GMBH Main goals

Slide 22

Slide 22 text

Architecture Android tvOS common common

Slide 23

Slide 23 text

CONFIDENTIAL FREELETICS GMBH Workout StateMachine inState { 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 )

Slide 24

Slide 24 text

CONFIDENTIAL FREELETICS GMBH Workout StateMachine inState { on { 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? )

Slide 25

Slide 25 text

CONFIDENTIAL FREELETICS GMBH Workout StateMachine class MyStateMachine ( initialState: MyState ): FlowReduxStateMachine(initialState) { inState { on { action, getState, setState -> setState { //create new state } } //on {...} } inState { observeWhileInState(someFlowWhichEmitsValues) { value, getState, setState -> setState { //create new state } } //on {...} } }

Slide 26

Slide 26 text

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(...) } } }

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

CONFIDENTIAL FREELETICS GMBH How much code did I share? Android Code: ~2500 LOC TvOS Code: ~1400 LOC Shared Code: ~4600 LOC

Slide 29

Slide 29 text

- 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

Slide 30

Slide 30 text

Would you use Kotlin Native in a real project? CONFIDENTIAL FREELETICS GMBH

Slide 31

Slide 31 text

Future?

Slide 32

Slide 32 text

CONFIDENTIAL FREELETICS GMBH Write once, run everywhere

Slide 33

Slide 33 text

CONFIDENTIAL FREELETICS GMBH Write once, run away!

Slide 34

Slide 34 text

CONFIDENTIAL FREELETICS GMBH Learn once, write anywhere

Slide 35

Slide 35 text

CONFIDENTIAL FREELETICS GMBH inState { observeWhileInState(countdownStopWatch.timeElapsed()) { ... } } inState { on { ... } }

Slide 36

Slide 36 text

CONFIDENTIAL FREELETICS GMBH class WorkoutScreen extends React.Component { render() { if (this.state instanceof CountdownState) return ; else if (this.state instanceof ExerciseState) return ; } }

Slide 37

Slide 37 text

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) } } }

Slide 38

Slide 38 text

CONFIDENTIAL FREELETICS GMBH @Composable fun WorkoutScreen(state : State) { when(state){ is CountdownState -> Countdown(state.timeLeft) is ExcerciseState -> Exercise(state) } }

Slide 39

Slide 39 text

Q&A

Slide 40

Slide 40 text

Demo

Slide 41

Slide 41 text

CONFIDENTIAL FREELETICS GMBH KEEP CALM AND SHIP IT