SlideShare una empresa de Scribd logo
1 de 52
MVVM with Kotlin:
Making iOS and Android apps as similar as possible
Malte Bucksch 1
Malte Bucksch 2
Goal: Sharing logic between native apps
Malte Bucksch 3
Agenda
I. The problem we’re solving
II. Comparison between architecture patterns
III. Our approach with MVVM
IV. How Kotlin helps us
V. Testing with MVVM
VI. Reusability of ViewModels
Malte Bucksch 4
Malte Bucksch 5
TEAM
Mixing business logic
and view logic
Testable
app architecture
Beautiful native UI
Awesome app Sharing logic between
Android and iOS
MVC: Model-View-Controller
Malte Bucksch 6
“Model View Controller (MVC) is one of the most
quoted (and most misquoted) patterns around”
– Martin Fowler (martinfowler.com)
Malte Bucksch 7
What is MVC after all?
Malte Bucksch 8
Wikipedia Apple Martin Fowler
Common practice
with MVC
Malte Bucksch 9
View View
ControllerFragment ViewController
MVVM: Model-View-ViewModel
Malte Bucksch 10
View View Model Model
Presentation
User Input
Business
Logic
Data
User action
Update
Update
Notify
Malte Bucksch 11
Data
View
ViewModel
Our approach with MVVM
• Platform-independent ViewModels
• Functional core using Input-to-Output mapping
Malte Bucksch 12
MVC
Malte Bucksch 13
View View
ControllerFragment ViewController
MVVM
Malte Bucksch 14
View
ViewModel
View
ViewModel
I have a dream today
Malte Bucksch 15
View
ViewModel
View
I have a dream today
Malte Bucksch 16
View
ViewModel
View
ViewModel
Let’s build a translation app
Malte Bucksch 17
Let’s build a translation app
Malte Bucksch 18
Deactivated
View Models
Malte Bucksch 19
View View Model
Inputs
Outputs
Malte Bucksch 20
ViewModel
Translate
Input
Output
Combine
with
german
text
Input
Output
f(input) = output
f(english) = german
Malte Bucksch 21
ViewModel Implementation Example
Malte Bucksch 22
ViewModel Implementation
Malte Bucksch 23
ReactiveX
RxJava / RxSwift
ViewModel Interface
24
interface TranslatorViewModelInput {
val englishText: BehaviorSubject<String>
val saveTrigger: PublishSubject<Unit>
}
interface TranslatorViewModelOutput {
val germanText: Observable<String>
val isSavingAllowed: Observable<Boolean>
val savedGermanTranslation: Observable<String>
}
interface TranslatorViewModel {
val input: TranslatorViewModelInput
val output: TranslatorViewModelOutput
}
class TranslatorViewModelImpl :
TranslatorViewModel,
TranslatorViewModelInput,
TranslatorViewModelOutput {
override val input = this
override val output = this
…
Usage
val viewModel: TranslatorViewModel =
createTranslatorViewModel()
viewModel.germanText
viewModel.englishText
viewModel.output.germanText
viewModel.input.englishText
ViewModel Implementation
Malte Bucksch 25
class TranslatorViewModelImpl : TranslatorViewModel, TranslatorViewModelInput, TranslatorViewModelOutput {
override val input = this
override val output = this
// *** inputs ***
override val englishText = BehaviorSubject.createDefault("")
override val saveTrigger = PublishSubject.create<Unit>()
// *** outputs ***
override val germanText = input.englishText
.map { TranslatorEngine.translateToGerman(it) }
override val isSavingAllowed = input.englishText
.map { !it.isEmpty() }
override val savedGermanTranslation =
input.saveTrigger.withLatestFrom(germanText)
.map { (_, german) -> german }
}
Binding to ViewModels via Kotlin Fragment
Malte Bucksch 26
// *** supply inputs ***
viewModel.input.englishText.receiveTextChangesFrom(englishInputEditText)
viewModel.input.saveTrigger.receiveClicksFrom(saveTextButton)
// *** subscribe to outputs ***
viewModel.output.germanText
.subscribe { germanOutputTextView.text = it }
viewModel.output.isSavingAllowed
.subscribe { saveTextButton.isEnabled = it }
viewModel.output.savedGermanTranslation
.subscribe { germanText ->
showMessage("Saved to clipboard")
germanText.saveToClipboard()
}
How Kotlin helps us
Malte Bucksch 27
Kotlin and Swift are very similar
Malte Bucksch 28
Shared Kotlin/Swift Features
• Optionals
• String Interpolation
• Extension functions
• Functional operations for lists/arrays
• Type inference
• Lambdas
• Type alias
• …
Malte Bucksch 29
Kotlin vs. Swift: Variables and constants
Malte Bucksch 30
var myVariable = 42
myVariable = 50
let myConstant = 42
var myVariable = 42
myVariable = 50
val myConstant = 42
Kotlin vs. Swift: String interpolation
Malte Bucksch 31
let apples = 3
let oranges = 5
let fruitSummary = "I have (apples + oranges) " + "pieces of fruit."
val apples = 3
val oranges = 5
val fruitSummary = "I have ${apples + oranges} " + "pieces of fruit."
Kotlin vs. Swift: Maps
Malte Bucksch 32
val occupations = mutableMapOf( "Malcolm" to "Captain",
"Kaylee" to "Mechanic")
occupations["Jayne"] = "Public Relations"
var occupations = [ "Malcolm": "Captain",
"Kaylee": "Mechanic"]
occupations["Jayne"] = "Public Relations"
Kotlin vs Swift ViewModel
Malte Bucksch 33
interface TranslatorViewModelInput {
val englishText: BehaviorSubject<String>
val saveTrigger: PublishSubject<Unit>
}
interface TranslatorViewModelOutput {
val germanText: Observable<String>
val isSavingAllowed: Observable<Boolean>
val savedGermanTranslation: Observable<String>
}
interface TranslatorViewModel {
val input: TranslatorViewModelInput
val output: TranslatorViewModelOutput
}
protocol TranslatorViewModelInput {
var englishText: BehaviorSubject<String> { get }
var saveTrigger: PublishSubject<Void> { get }
}
protocol TranslatorViewModelOutput {
var germanText: Observable<String> { get }
var isSavingAllowed: Observable<Bool> { get }
var saveGermanTranslation: Observable<String> { get }
}
protocol TranslatorViewModel {
var input: TranslatorViewModelInput { get }
var output: TranslatorViewModelOutput { get }
}
Kotlin vs Swift ViewModel
Malte Bucksch 34
class TranslatorViewModelImpl:
TranslatorViewModel,
TranslatorViewModelInput,
TranslatorViewModelOutput {
var input: TranslatorViewModelInput { return self }
var output: TranslatorViewModelOutput { return self }
// MARK: - Inputs
var englishText = BehaviourSubject<String>(value: "")
var saveTrigger = PublishSubject<Void>()
…
class TranslatorViewModelImpl:
TranslatorViewModel,
TranslatorViewModelInput,
TranslatorViewModelOutput {
override val input = this
override val output = this
// *** inputs ***
override val englishText =
BehaviorSubject.createDefault("")
override val saveTrigger =
PublishSubject.create<Unit>()
…
Kotlin vs Swift ViewModel
Malte Bucksch 35
…
// MARK: - Outputs
lazy var germanText: Observable<String> = {
input.englishText
.map { TranslatorEngine.translateToGerman($0) }
}()
lazy var isSavingAllowed: Observable<Bool> = {
input.englishText
.map { $0.isEmpty }
}()
…
// *** outputs ***
override val germanText = input.englishText
.map { TranslatorEngine.translateToGerman(it) }
override val isSavingAllowed = input.englishText
.map { !it.isEmpty() }
override val savedGermanTranslation =
input.saveTrigger.withLatestFrom(germanText)
.map { (_, german) -> german }
Testing ViewModels
Malte Bucksch 36
View Model Tests
Malte Bucksch 37
View View Model
Input
Output
View Model Tests
Malte Bucksch 38
Unit Test View Model
Input
Output
ViewModel Tests
Malte Bucksch 39
@Test
fun testTranslation() {
val viewModel: TranslatorViewModel = TranslatorViewModelImpl()
viewModel.input.englishText.onNext("Dog")
viewModel.output.germanText.test().assertValue("Hund")
}
Local unit tests for UI testing
Malte Bucksch 40
Fast local unit tests Avoid UI testing frameworks
ViewModel reusability
Malte Bucksch 41
ViewModel reusability
Malte Bucksch 42
Android View View Model
Input
Output
ViewModel reusability
Malte Bucksch 43
Android Fragment View Model
Input
Output
ViewModel reusability
Malte Bucksch 44
Unit Test View Model
Input
Output
ViewModel reusability
Malte Bucksch 45
JavaFX View View Model
Input
Output
ViewModel reusability
Malte Bucksch 46
Java/Kotlin REST API View Model
Input
Output
ViewModel reusability
Malte Bucksch 47
Kotlinx (HTML) View View Model
Input
Output
One ViewModel to rule them all
Malte Bucksch 48
Wrap up
Advantages
• Logic can be reused within iOS and Android
• Functional way of writing UI with isolated side effects
• Great testability
Malte Bucksch 49
Wrap up
Advantages
• Logic can be reused within iOS and Android
• Functional way of writing UI with isolated side effects
• Great testability
Disadvantages
• Boiler plate for interfaces
• Steep learning curve
• Less intuitive if programmer is not used to functional-style programming
Malte Bucksch 50
Wrap up
Improvement points
• Optimize project workflow to make people reuse code
• Write ViewModels in exact same programming language
Malte Bucksch 51
Questions?
More resources on MVVM
https://github.com/quickbirdstudios
https://github.com/kickstarter/android-oss
https://github.com/kickstarter/ios-oss
https://bit.ly/2qSFPqO (Talk at UI Konf about Swift/Kotlin similarity)
malte@quickbirdstudios.com
Malte Bucksch 52

Más contenido relacionado

La actualidad más candente

379008-rc217-functionalprogramming
379008-rc217-functionalprogramming379008-rc217-functionalprogramming
379008-rc217-functionalprogramming
Luis Atencio
 
08. session 08 intoduction to javascript
08. session 08   intoduction to javascript08. session 08   intoduction to javascript
08. session 08 intoduction to javascript
Phúc Đỗ
 

La actualidad más candente (12)

Railway Orientated Programming In C#
Railway Orientated Programming In C#Railway Orientated Programming In C#
Railway Orientated Programming In C#
 
Beginner’s tutorial (part 1) integrate redux form in react native application
Beginner’s tutorial (part 1) integrate redux form in react native applicationBeginner’s tutorial (part 1) integrate redux form in react native application
Beginner’s tutorial (part 1) integrate redux form in react native application
 
Silverlight 2 for Developers - TechEd New Zealand 2008
Silverlight 2 for Developers - TechEd New Zealand 2008Silverlight 2 for Developers - TechEd New Zealand 2008
Silverlight 2 for Developers - TechEd New Zealand 2008
 
379008-rc217-functionalprogramming
379008-rc217-functionalprogramming379008-rc217-functionalprogramming
379008-rc217-functionalprogramming
 
Client sidescripting javascript
Client sidescripting javascriptClient sidescripting javascript
Client sidescripting javascript
 
Fp201 unit2 1
Fp201 unit2 1Fp201 unit2 1
Fp201 unit2 1
 
Asp.net mvc training
Asp.net mvc trainingAsp.net mvc training
Asp.net mvc training
 
Angular Mini Hackathon Code Talks 2019
Angular Mini Hackathon Code Talks 2019Angular Mini Hackathon Code Talks 2019
Angular Mini Hackathon Code Talks 2019
 
General structure of c++
General structure of c++General structure of c++
General structure of c++
 
08. session 08 intoduction to javascript
08. session 08   intoduction to javascript08. session 08   intoduction to javascript
08. session 08 intoduction to javascript
 
Function
FunctionFunction
Function
 
Angular 2.0 - What to expect
Angular 2.0 - What to expectAngular 2.0 - What to expect
Angular 2.0 - What to expect
 

Similar a MVVM with Kotlin: Making iOS and Android apps as similar as possible

MEF Deep Dive by Piotr Wlodek
MEF Deep Dive by Piotr WlodekMEF Deep Dive by Piotr Wlodek
MEF Deep Dive by Piotr Wlodek
infusiondev
 

Similar a MVVM with Kotlin: Making iOS and Android apps as similar as possible (20)

iOS
iOSiOS
iOS
 
Compose in Theory
Compose in TheoryCompose in Theory
Compose in Theory
 
How to build to do app using vue composition api and vuex 4 with typescript
How to build to do app using vue composition api and vuex 4 with typescriptHow to build to do app using vue composition api and vuex 4 with typescript
How to build to do app using vue composition api and vuex 4 with typescript
 
How to create an Angular builder
How to create an Angular builderHow to create an Angular builder
How to create an Angular builder
 
Part II: LLVM Intermediate Representation
Part II: LLVM Intermediate RepresentationPart II: LLVM Intermediate Representation
Part II: LLVM Intermediate Representation
 
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
 
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
 
MEF Deep Dive by Piotr Wlodek
MEF Deep Dive by Piotr WlodekMEF Deep Dive by Piotr Wlodek
MEF Deep Dive by Piotr Wlodek
 
Compose Camp Day 3 PPT.pptx.pdf
Compose Camp Day 3 PPT.pptx.pdfCompose Camp Day 3 PPT.pptx.pdf
Compose Camp Day 3 PPT.pptx.pdf
 
Why Spring <3 Kotlin
Why Spring <3 KotlinWhy Spring <3 Kotlin
Why Spring <3 Kotlin
 
In-Depth Model/View with QML
In-Depth Model/View with QMLIn-Depth Model/View with QML
In-Depth Model/View with QML
 
Net conf BG xamarin lecture
Net conf BG xamarin lectureNet conf BG xamarin lecture
Net conf BG xamarin lecture
 
Ekon25 mORMot 2 Server-Side Notifications
Ekon25 mORMot 2 Server-Side NotificationsEkon25 mORMot 2 Server-Side Notifications
Ekon25 mORMot 2 Server-Side Notifications
 
Real-world Model-View-ViewModel for WPF
Real-world Model-View-ViewModel for WPFReal-world Model-View-ViewModel for WPF
Real-world Model-View-ViewModel for WPF
 
Functionnal view modelling
Functionnal view modelling Functionnal view modelling
Functionnal view modelling
 
La programmation concurrente par flux de données
La programmation concurrente par flux de donnéesLa programmation concurrente par flux de données
La programmation concurrente par flux de données
 
iOS_Presentation
iOS_PresentationiOS_Presentation
iOS_Presentation
 
Protocol-Oriented MVVM (extended edition)
Protocol-Oriented MVVM (extended edition)Protocol-Oriented MVVM (extended edition)
Protocol-Oriented MVVM (extended edition)
 
An Introductory course on Verilog HDL-Verilog hdl ppr
An Introductory course on Verilog HDL-Verilog hdl pprAn Introductory course on Verilog HDL-Verilog hdl ppr
An Introductory course on Verilog HDL-Verilog hdl ppr
 
using Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API'susing Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API's
 

Último

Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
masabamasaba
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
masabamasaba
 
Abortion Pill Prices Boksburg [(+27832195400*)] 🏥 Women's Abortion Clinic in ...
Abortion Pill Prices Boksburg [(+27832195400*)] 🏥 Women's Abortion Clinic in ...Abortion Pill Prices Boksburg [(+27832195400*)] 🏥 Women's Abortion Clinic in ...
Abortion Pill Prices Boksburg [(+27832195400*)] 🏥 Women's Abortion Clinic in ...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
VictoriaMetrics
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
masabamasaba
 

Último (20)

WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go Platformless
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
WSO2Con2024 - GitOps in Action: Navigating Application Deployment in the Plat...
WSO2Con2024 - GitOps in Action: Navigating Application Deployment in the Plat...WSO2Con2024 - GitOps in Action: Navigating Application Deployment in the Plat...
WSO2Con2024 - GitOps in Action: Navigating Application Deployment in the Plat...
 
WSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaS
 
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
 
WSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security ProgramWSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security Program
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
 
Abortion Pill Prices Boksburg [(+27832195400*)] 🏥 Women's Abortion Clinic in ...
Abortion Pill Prices Boksburg [(+27832195400*)] 🏥 Women's Abortion Clinic in ...Abortion Pill Prices Boksburg [(+27832195400*)] 🏥 Women's Abortion Clinic in ...
Abortion Pill Prices Boksburg [(+27832195400*)] 🏥 Women's Abortion Clinic in ...
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
 

MVVM with Kotlin: Making iOS and Android apps as similar as possible

  • 1. MVVM with Kotlin: Making iOS and Android apps as similar as possible Malte Bucksch 1
  • 3. Goal: Sharing logic between native apps Malte Bucksch 3
  • 4. Agenda I. The problem we’re solving II. Comparison between architecture patterns III. Our approach with MVVM IV. How Kotlin helps us V. Testing with MVVM VI. Reusability of ViewModels Malte Bucksch 4
  • 5. Malte Bucksch 5 TEAM Mixing business logic and view logic Testable app architecture Beautiful native UI Awesome app Sharing logic between Android and iOS
  • 7. “Model View Controller (MVC) is one of the most quoted (and most misquoted) patterns around” – Martin Fowler (martinfowler.com) Malte Bucksch 7
  • 8. What is MVC after all? Malte Bucksch 8 Wikipedia Apple Martin Fowler
  • 9. Common practice with MVC Malte Bucksch 9 View View ControllerFragment ViewController
  • 10. MVVM: Model-View-ViewModel Malte Bucksch 10 View View Model Model Presentation User Input Business Logic Data User action Update Update Notify
  • 12. Our approach with MVVM • Platform-independent ViewModels • Functional core using Input-to-Output mapping Malte Bucksch 12
  • 13. MVC Malte Bucksch 13 View View ControllerFragment ViewController
  • 15. I have a dream today Malte Bucksch 15 View ViewModel View
  • 16. I have a dream today Malte Bucksch 16 View ViewModel View ViewModel
  • 17. Let’s build a translation app Malte Bucksch 17
  • 18. Let’s build a translation app Malte Bucksch 18 Deactivated
  • 19. View Models Malte Bucksch 19 View View Model Inputs Outputs
  • 21. f(input) = output f(english) = german Malte Bucksch 21
  • 23. ViewModel Implementation Malte Bucksch 23 ReactiveX RxJava / RxSwift
  • 24. ViewModel Interface 24 interface TranslatorViewModelInput { val englishText: BehaviorSubject<String> val saveTrigger: PublishSubject<Unit> } interface TranslatorViewModelOutput { val germanText: Observable<String> val isSavingAllowed: Observable<Boolean> val savedGermanTranslation: Observable<String> } interface TranslatorViewModel { val input: TranslatorViewModelInput val output: TranslatorViewModelOutput } class TranslatorViewModelImpl : TranslatorViewModel, TranslatorViewModelInput, TranslatorViewModelOutput { override val input = this override val output = this … Usage val viewModel: TranslatorViewModel = createTranslatorViewModel() viewModel.germanText viewModel.englishText viewModel.output.germanText viewModel.input.englishText
  • 25. ViewModel Implementation Malte Bucksch 25 class TranslatorViewModelImpl : TranslatorViewModel, TranslatorViewModelInput, TranslatorViewModelOutput { override val input = this override val output = this // *** inputs *** override val englishText = BehaviorSubject.createDefault("") override val saveTrigger = PublishSubject.create<Unit>() // *** outputs *** override val germanText = input.englishText .map { TranslatorEngine.translateToGerman(it) } override val isSavingAllowed = input.englishText .map { !it.isEmpty() } override val savedGermanTranslation = input.saveTrigger.withLatestFrom(germanText) .map { (_, german) -> german } }
  • 26. Binding to ViewModels via Kotlin Fragment Malte Bucksch 26 // *** supply inputs *** viewModel.input.englishText.receiveTextChangesFrom(englishInputEditText) viewModel.input.saveTrigger.receiveClicksFrom(saveTextButton) // *** subscribe to outputs *** viewModel.output.germanText .subscribe { germanOutputTextView.text = it } viewModel.output.isSavingAllowed .subscribe { saveTextButton.isEnabled = it } viewModel.output.savedGermanTranslation .subscribe { germanText -> showMessage("Saved to clipboard") germanText.saveToClipboard() }
  • 27. How Kotlin helps us Malte Bucksch 27
  • 28. Kotlin and Swift are very similar Malte Bucksch 28
  • 29. Shared Kotlin/Swift Features • Optionals • String Interpolation • Extension functions • Functional operations for lists/arrays • Type inference • Lambdas • Type alias • … Malte Bucksch 29
  • 30. Kotlin vs. Swift: Variables and constants Malte Bucksch 30 var myVariable = 42 myVariable = 50 let myConstant = 42 var myVariable = 42 myVariable = 50 val myConstant = 42
  • 31. Kotlin vs. Swift: String interpolation Malte Bucksch 31 let apples = 3 let oranges = 5 let fruitSummary = "I have (apples + oranges) " + "pieces of fruit." val apples = 3 val oranges = 5 val fruitSummary = "I have ${apples + oranges} " + "pieces of fruit."
  • 32. Kotlin vs. Swift: Maps Malte Bucksch 32 val occupations = mutableMapOf( "Malcolm" to "Captain", "Kaylee" to "Mechanic") occupations["Jayne"] = "Public Relations" var occupations = [ "Malcolm": "Captain", "Kaylee": "Mechanic"] occupations["Jayne"] = "Public Relations"
  • 33. Kotlin vs Swift ViewModel Malte Bucksch 33 interface TranslatorViewModelInput { val englishText: BehaviorSubject<String> val saveTrigger: PublishSubject<Unit> } interface TranslatorViewModelOutput { val germanText: Observable<String> val isSavingAllowed: Observable<Boolean> val savedGermanTranslation: Observable<String> } interface TranslatorViewModel { val input: TranslatorViewModelInput val output: TranslatorViewModelOutput } protocol TranslatorViewModelInput { var englishText: BehaviorSubject<String> { get } var saveTrigger: PublishSubject<Void> { get } } protocol TranslatorViewModelOutput { var germanText: Observable<String> { get } var isSavingAllowed: Observable<Bool> { get } var saveGermanTranslation: Observable<String> { get } } protocol TranslatorViewModel { var input: TranslatorViewModelInput { get } var output: TranslatorViewModelOutput { get } }
  • 34. Kotlin vs Swift ViewModel Malte Bucksch 34 class TranslatorViewModelImpl: TranslatorViewModel, TranslatorViewModelInput, TranslatorViewModelOutput { var input: TranslatorViewModelInput { return self } var output: TranslatorViewModelOutput { return self } // MARK: - Inputs var englishText = BehaviourSubject<String>(value: "") var saveTrigger = PublishSubject<Void>() … class TranslatorViewModelImpl: TranslatorViewModel, TranslatorViewModelInput, TranslatorViewModelOutput { override val input = this override val output = this // *** inputs *** override val englishText = BehaviorSubject.createDefault("") override val saveTrigger = PublishSubject.create<Unit>() …
  • 35. Kotlin vs Swift ViewModel Malte Bucksch 35 … // MARK: - Outputs lazy var germanText: Observable<String> = { input.englishText .map { TranslatorEngine.translateToGerman($0) } }() lazy var isSavingAllowed: Observable<Bool> = { input.englishText .map { $0.isEmpty } }() … // *** outputs *** override val germanText = input.englishText .map { TranslatorEngine.translateToGerman(it) } override val isSavingAllowed = input.englishText .map { !it.isEmpty() } override val savedGermanTranslation = input.saveTrigger.withLatestFrom(germanText) .map { (_, german) -> german }
  • 37. View Model Tests Malte Bucksch 37 View View Model Input Output
  • 38. View Model Tests Malte Bucksch 38 Unit Test View Model Input Output
  • 39. ViewModel Tests Malte Bucksch 39 @Test fun testTranslation() { val viewModel: TranslatorViewModel = TranslatorViewModelImpl() viewModel.input.englishText.onNext("Dog") viewModel.output.germanText.test().assertValue("Hund") }
  • 40. Local unit tests for UI testing Malte Bucksch 40 Fast local unit tests Avoid UI testing frameworks
  • 42. ViewModel reusability Malte Bucksch 42 Android View View Model Input Output
  • 43. ViewModel reusability Malte Bucksch 43 Android Fragment View Model Input Output
  • 44. ViewModel reusability Malte Bucksch 44 Unit Test View Model Input Output
  • 45. ViewModel reusability Malte Bucksch 45 JavaFX View View Model Input Output
  • 46. ViewModel reusability Malte Bucksch 46 Java/Kotlin REST API View Model Input Output
  • 47. ViewModel reusability Malte Bucksch 47 Kotlinx (HTML) View View Model Input Output
  • 48. One ViewModel to rule them all Malte Bucksch 48
  • 49. Wrap up Advantages • Logic can be reused within iOS and Android • Functional way of writing UI with isolated side effects • Great testability Malte Bucksch 49
  • 50. Wrap up Advantages • Logic can be reused within iOS and Android • Functional way of writing UI with isolated side effects • Great testability Disadvantages • Boiler plate for interfaces • Steep learning curve • Less intuitive if programmer is not used to functional-style programming Malte Bucksch 50
  • 51. Wrap up Improvement points • Optimize project workflow to make people reuse code • Write ViewModels in exact same programming language Malte Bucksch 51
  • 52. Questions? More resources on MVVM https://github.com/quickbirdstudios https://github.com/kickstarter/android-oss https://github.com/kickstarter/ios-oss https://bit.ly/2qSFPqO (Talk at UI Konf about Swift/Kotlin similarity) malte@quickbirdstudios.com Malte Bucksch 52

Notas del editor

  1. No side effects Same input always same output