SlideShare a Scribd company logo
Functional
ViewModelling
Using RxSwift
Why?
Why?
• Expressive 

• Testable

• No boilerPlate
What is RxSwift
• Implementation of ReactiveX

• Observable<Element>

• Operators (map, filter)

• Composable (Merge, zip)
MVVM
There was MVC
*This is not actually how MVC works, just making a point on what sometimes happens in projects
MVVM
Decouple business logic and view implementation
Many faces of MVVM
• Functions as input
Many faces of MVVM
• Functions as input
Many faces of MVVM
• Subjects / Relays as inputs
class MessagesViewModel {
!// Input
let textInput: Observable<String>
let sendButton: Observable<Void>
let selectItem: Observable<MessageRepresentation>
!// Output
let dataSource: Observable<[MessageRepresentation]>
let isLoading: Observable<Bool>
[…]
}
Many faces of MVVM
• Subjects / Relays as inputs
class MessagesViewModel {
!// Input
let textInput: Observable<String>
let sendButton: Observable<Void>
let selectItem: Observable<MessageRepresentation>
!// Output
let dataSource: Observable<[MessageRepresentation]>
let isLoading: Observable<Bool>
[…]
}
Many faces of MVVM
• Subjects / Relays as inputs
class MessagesViewModel {
!// Input
let textInput: Observable<String>
let sendButton: Observable<Void>
let selectItem: Observable<MessageRepresentation>
!// Output
let dataSource: Observable<[MessageRepresentation]>
let isLoading: Observable<Bool>
[…]
}
Functions as ViewModels
• Input and Output per viewModel
struct MessagesInput {
let textInput: Observable<String>
let sendButton: Observable<Void>
let selectItem: Observable<MessageRepresentation>
}
struct MessagesOutput {
let dataSource: Observable<[MessageRepresentation]>
let isLoading: Observable<Bool>
}
• From input to output
Functions as ViewModels
struct MessagesInput {
let textInput: Observable<String>
let sendButton: Observable<Void>
let selectItem: Observable<MessageRepresentation>
}
struct MessagesOutput {
let dataSource: Observable<[MessageRepresentation]>
let isLoading: Observable<Bool>
}
• ViewModelFactory
var viewModelFactory: (MessagesInput) !-> MessagesOutput
= { _ in fatalError("Missing viewModel factory") }
override func viewDidLoad() {
super.viewDidLoad()
let input = MessagesInput(textInput: textInput.rx.text.unwrap(),
sendButton: sendButton.rx.tap.asVoid(),
selectItem: .empty())
let output = viewModelFactory(input)
bag.insert(
output.dataSource.bind(to: dataSource),
output.isLoading.bind(to: refreshControl.rx.isRefreshing)
)
}
Functions as ViewModels
struct MessagesInput {
let textInput: Observable<String>
let sendButton: Observable<Void>
let selectItem: Observable<MessageRepresentation>
}
struct MessagesOutput {
let dataSource: Observable<[MessageRepresentation]>
let isLoading: Observable<Bool>
}
• ViewModelFactory
var viewModelFactory: (MessagesInput) !-> MessagesOutput
= { _ in fatalError("Missing viewModel factory") }
override func viewDidLoad() {
super.viewDidLoad()
let input = MessagesInput(textInput: textInput.rx.text.unwrap(),
sendButton: sendButton.rx.tap.asVoid(),
selectItem: .empty())
let output = viewModelFactory(input)
bag.insert(
output.dataSource.bind(to: dataSource),
output.isLoading.bind(to: refreshControl.rx.isRefreshing)
)
}
Functions as ViewModels
struct MessagesInput {
let textInput: Observable<String>
let sendButton: Observable<Void>
let selectItem: Observable<MessageRepresentation>
}
struct MessagesOutput {
let dataSource: Observable<[MessageRepresentation]>
let isLoading: Observable<Bool>
}
• ViewModelFactory
var viewModelFactory: (MessagesInput) !-> MessagesOutput
= { _ in fatalError("Missing viewModel factory") }
override func viewDidLoad() {
super.viewDidLoad()
let input = MessagesInput(textInput: textInput.rx.text.unwrap(),
sendButton: sendButton.rx.tap.asVoid(),
selectItem: .empty())
let output = viewModelFactory(input)
bag.insert(
output.dataSource.bind(to: dataSource),
output.isLoading.bind(to: refreshControl.rx.isRefreshing)
)
}
Functions as ViewModels
struct MessagesInput {
let textInput: Observable<String>
let sendButton: Observable<Void>
let selectItem: Observable<MessageRepresentation>
}
struct MessagesOutput {
let dataSource: Observable<[MessageRepresentation]>
let isLoading: Observable<Bool>
}
• ViewModelFactory
var viewModelFactory: (MessagesInput) !-> MessagesOutput
= { _ in fatalError("Missing viewModel factory") }
override func viewDidLoad() {
super.viewDidLoad()
let input = MessagesInput(textInput: textInput.rx.text.unwrap(),
sendButton: sendButton.rx.tap.asVoid(),
selectItem: .empty())
let output = viewModelFactory(input)
bag.insert(
output.dataSource.bind(to: dataSource),
output.isLoading.bind(to: refreshControl.rx.isRefreshing)
)
}
Functions as ViewModels
struct MessagesInput {
let textInput: Observable<String>
let sendButton: Observable<Void>
let selectItem: Observable<MessageRepresentation>
}
struct MessagesOutput {
let dataSource: Observable<[MessageRepresentation]>
let isLoading: Observable<Bool>
}
• ViewModelFactory
var viewModelFactory: (MessagesInput) !-> MessagesOutput
= { _ in fatalError("Missing viewModel factory") }
override func viewDidLoad() {
super.viewDidLoad()
let input = MessagesInput(textInput: textInput.rx.text.unwrap(),
sendButton: sendButton.rx.tap.asVoid(),
selectItem: .empty())
let output = viewModelFactory(input)
bag.insert(
output.dataSource.bind(to: dataSource),
output.isLoading.bind(to: refreshControl.rx.isRefreshing)
)
}
Functions as ViewModels
struct MessagesInput {
let textInput: Observable<String>
let sendButton: Observable<Void>
let selectItem: Observable<MessageRepresentation>
}
struct MessagesOutput {
let dataSource: Observable<[MessageRepresentation]>
let isLoading: Observable<Bool>
}
• The ViewModel itself
Functions as ViewModels
struct MessagesInput {
let textInput: Observable<String>
let sendButton: Observable<Void>
let selectItem: Observable<MessageRepresentation>
}
struct MessagesOutput {
let dataSource: Observable<[MessageRepresentation]>
let isLoading: Observable<Bool>
}
func messagesViewModel(messageStream: MessageStream)
!-> (_ input: MessagesInput) !-> (MessagesOutput) {
{ (input: MessagesInput) in
let messages = messageStream.messages
let isLoading = messageStream.isLoading
return MessagesOutput(dataSource: messages, isLoading: isLoading)
}
}
Functions as ViewModels
• The ViewModel itself
struct MessagesInput {
let textInput: Observable<String>
let sendButton: Observable<Void>
let selectItem: Observable<MessageRepresentation>
}
struct MessagesOutput {
let dataSource: Observable<[MessageRepresentation]>
let isLoading: Observable<Bool>
}
func messagesViewModel(messageStream: MessageStream)
!-> (_ input: MessagesInput) !-> (MessagesOutput) {
{ (input: MessagesInput) in
let messages = messageStream.messages
let isLoading = messageStream.isLoading
return MessagesOutput(dataSource: messages, isLoading: isLoading)
}
}
Functions as ViewModels
• The ViewModel itself
struct MessagesInput {
let textInput: Observable<String>
let sendButton: Observable<Void>
let selectItem: Observable<MessageRepresentation>
}
struct MessagesOutput {
let dataSource: Observable<[MessageRepresentation]>
let isLoading: Observable<Bool>
}
func messagesViewModel(messageStream: MessageStream)
!-> (_ input: MessagesInput) !-> (MessagesOutput) {
{ (input: MessagesInput) in
let messages = messageStream.messages
let isLoading = messageStream.isLoading
return MessagesOutput(dataSource: messages, isLoading: isLoading)
}
}
Functions as ViewModels
• The ViewModel itself
struct MessagesInput {
let textInput: Observable<String>
let sendButton: Observable<Void>
let selectItem: Observable<MessageRepresentation>
}
struct MessagesOutput {
let dataSource: Observable<[MessageRepresentation]>
let isLoading: Observable<Bool>
}
Functions as ViewModels
• Actions / Side effects
func messagesViewModel(messageStream: MessageStream)
!-> (_ input: MessagesInput)
!-> (output: MessagesOutput, actions: Observable<MessagesAction>) {
{ (input: MessagesInput) in
let messages = messageStream.messages.mapMany(MessageRepresentation.init)
let isLoading = messageStream.isLoading
let output = MessagesOutput(dataSource: messages, isLoading: isLoading)
let newMessage = input.sendButton
.withLatestFrom(input.textInput)
.filter { $0.isEmpty }
.map(MessagesAction.newMessage)
let displayMessage = input.selectItem.map(MessagesAction.message)
let effects = Observable.merge(newMessage, displayMessage)
return ( output, effects)
}
}
Functions as ViewModels
• Actions / Side effects
func messagesViewModel(messageStream: MessageStream)
!-> (_ input: MessagesInput)
!-> (output: MessagesOutput, actions: Observable<MessagesAction>) {
{ (input: MessagesInput) in
let messages = messageStream.messages.mapMany(MessageRepresentation.init)
let isLoading = messageStream.isLoading
let output = MessagesOutput(dataSource: messages, isLoading: isLoading)
let newMessage = input.sendButton
.withLatestFrom(input.textInput)
.filter { $0.isEmpty }
.map(MessagesAction.newMessage)
let displayMessage = input.selectItem.map(MessagesAction.message)
let effects = Observable.merge(newMessage, displayMessage)
return ( output, effects)
}
}
Functions as ViewModels
• Actions / Side effects
func messagesViewModel(messageStream: MessageStream)
!-> (_ input: MessagesInput)
!-> (output: MessagesOutput, actions: Observable<MessagesAction>) {
{ (input: MessagesInput) in
let messages = messageStream.messages.mapMany(MessageRepresentation.init)
let isLoading = messageStream.isLoading
let output = MessagesOutput(dataSource: messages, isLoading: isLoading)
let newMessage = input.sendButton
.withLatestFrom(input.textInput)
.filter { $0.isEmpty }
.map(MessagesAction.newMessage)
let displayMessage = input.selectItem.map(MessagesAction.message)
let effects = Observable.merge(newMessage, displayMessage)
return ( output, effects)
}
}
Functions as ViewModels
• Actions / Side effects
Everything you need before
looking at the implementation
Easy Testing
• Unit tests
Sources
• Daniel Tartaglia 

• https://github.com/danielt1263/RxMyCoordinator

• http://reactivex.io

• https://medium.com/flawless-app-stories/simplifying-
rxswift-code-78071d5b780

• https://github.com/ReactiveX/RxSwift

• http://slack.rxswift.org/
Questions?
Injection
protocol HasViewModel: class {
associatedtype Inputs
associatedtype Outputs
var viewModelFactory: (Inputs) !-> Outputs { get set }
}
extension HasViewModel {
func installOutputViewModel<Action>(factory: @escaping (Inputs) !-> (Outputs,
Observable<Action>))
!-> Observable<Action> {
return Observable.create { [weak self] observer in
let disposable = CompositeDisposable()
self!?.viewModelFactory = { inputs in
let vm = factory(inputs)
_ = disposable.insert(vm.1.bind(to: observer))
return vm.0
}
return disposable
}.bindToInterstitials()
}
}
Injection - 2
Coordinator Example:
func homeCoordinator(navigationController: UINavigationController?) {
let homeViewController = navigationController!?.topViewController as! HomeViewController
let actions = homeViewController.installOutputViewModel(
factory: homeViewModel(api: Api.network)
)
_ = actions.bind(onNext: actionHandler)
}

More Related Content

PPT
JavaScript
PPTX
Test and profile your Windows Phone 8 App
PPTX
Academy PRO: ASP .NET Core MVC
PDF
Angular JS2 Training Session #1
PPTX
java script
PPTX
Angular 2 Architecture (Bucharest 26/10/2016)
PDF
Java Web Programming [8/9] : JSF and AJAX
PPTX
Angular 2 NgModule
JavaScript
Test and profile your Windows Phone 8 App
Academy PRO: ASP .NET Core MVC
Angular JS2 Training Session #1
java script
Angular 2 Architecture (Bucharest 26/10/2016)
Java Web Programming [8/9] : JSF and AJAX
Angular 2 NgModule

What's hot (19)

PPTX
Java Servlet
PPTX
Database connect
PPTX
JSP- JAVA SERVER PAGES
PDF
Java Programming - 08 java threading
PDF
Java Web Programming [5/9] : EL, JSTL and Custom Tags
PDF
Designing a JavaFX Mobile application
PPT
ExtJs Basic Part-1
PPT
Intorduction of Playframework
PDF
Documenting from the Trenches
PPTX
Angular 2.0 forms
PPTX
Asp.NET MVC
PDF
Filtering data with D2W
PDF
Template rendering in rails
ODP
Creating a Java EE 7 Websocket Chat Application
PDF
[FEConf Korea 2017]Angular 컴포넌트 대화법
PDF
Spring 3: What's New
PDF
G*ワークショップ in 仙台 Grails(とことん)入門
PPTX
Modules and injector
PDF
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
Java Servlet
Database connect
JSP- JAVA SERVER PAGES
Java Programming - 08 java threading
Java Web Programming [5/9] : EL, JSTL and Custom Tags
Designing a JavaFX Mobile application
ExtJs Basic Part-1
Intorduction of Playframework
Documenting from the Trenches
Angular 2.0 forms
Asp.NET MVC
Filtering data with D2W
Template rendering in rails
Creating a Java EE 7 Websocket Chat Application
[FEConf Korea 2017]Angular 컴포넌트 대화법
Spring 3: What's New
G*ワークショップ in 仙台 Grails(とことん)入門
Modules and injector
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
Ad

Similar to Functionnal view modelling (20)

PDF
Introduction to VIPER Architecture
PDF
[22]Efficient and Testable MVVM pattern
PDF
Protocol-Oriented MVVM (extended edition)
PPTX
MV* presentation frameworks in Javascript: en garde, pret, allez!
PPTX
Présentation et bonnes pratiques du pattern MVVM - MIC Belgique
PDF
Protocol Oriented MVVM - Auckland iOS Meetup
PPTX
MVC & SQL_In_1_Hour
PDF
How to create an Angular builder
PPTX
ASP.NET MVC 5 - EF 6 - VS2015
PDF
What is your money doing?
PPTX
Fundaments of Knockout js
PDF
Medium TechTalk — iOS
PDF
Single page webapps & javascript-testing
PDF
Knockoutjs databinding
PPTX
Asp.net mvc training
PPTX
Academy PRO: React JS
PPTX
unit4 wp.pptxjvlbpuvghuigv8ytg2ugvugvuygv
PPTX
Web Technologies - forms and actions
PDF
Advanced #2 networking
Introduction to VIPER Architecture
[22]Efficient and Testable MVVM pattern
Protocol-Oriented MVVM (extended edition)
MV* presentation frameworks in Javascript: en garde, pret, allez!
Présentation et bonnes pratiques du pattern MVVM - MIC Belgique
Protocol Oriented MVVM - Auckland iOS Meetup
MVC & SQL_In_1_Hour
How to create an Angular builder
ASP.NET MVC 5 - EF 6 - VS2015
What is your money doing?
Fundaments of Knockout js
Medium TechTalk — iOS
Single page webapps & javascript-testing
Knockoutjs databinding
Asp.net mvc training
Academy PRO: React JS
unit4 wp.pptxjvlbpuvghuigv8ytg2ugvugvuygv
Web Technologies - forms and actions
Advanced #2 networking
Ad

Recently uploaded (20)

PDF
Upgrade and Innovation Strategies for SAP ERP Customers
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PPTX
Odoo POS Development Services by CandidRoot Solutions
PDF
Softaken Excel to vCard Converter Software.pdf
PDF
Design an Analysis of Algorithms I-SECS-1021-03
PPTX
ai tools demonstartion for schools and inter college
PDF
How Creative Agencies Leverage Project Management Software.pdf
PPTX
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
PPTX
Online Work Permit System for Fast Permit Processing
PDF
2025 Textile ERP Trends: SAP, Odoo & Oracle
PPTX
CHAPTER 2 - PM Management and IT Context
PDF
Nekopoi APK 2025 free lastest update
PPT
Introduction Database Management System for Course Database
PDF
Navsoft: AI-Powered Business Solutions & Custom Software Development
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PDF
Design an Analysis of Algorithms II-SECS-1021-03
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PDF
medical staffing services at VALiNTRY
Upgrade and Innovation Strategies for SAP ERP Customers
How to Choose the Right IT Partner for Your Business in Malaysia
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
Odoo POS Development Services by CandidRoot Solutions
Softaken Excel to vCard Converter Software.pdf
Design an Analysis of Algorithms I-SECS-1021-03
ai tools demonstartion for schools and inter college
How Creative Agencies Leverage Project Management Software.pdf
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
Online Work Permit System for Fast Permit Processing
2025 Textile ERP Trends: SAP, Odoo & Oracle
CHAPTER 2 - PM Management and IT Context
Nekopoi APK 2025 free lastest update
Introduction Database Management System for Course Database
Navsoft: AI-Powered Business Solutions & Custom Software Development
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
Design an Analysis of Algorithms II-SECS-1021-03
Odoo Companies in India – Driving Business Transformation.pdf
medical staffing services at VALiNTRY

Functionnal view modelling

  • 3. Why? • Expressive • Testable • No boilerPlate
  • 4. What is RxSwift • Implementation of ReactiveX • Observable<Element> • Operators (map, filter) • Composable (Merge, zip)
  • 5. MVVM There was MVC *This is not actually how MVC works, just making a point on what sometimes happens in projects
  • 6. MVVM Decouple business logic and view implementation
  • 7. Many faces of MVVM • Functions as input
  • 8. Many faces of MVVM • Functions as input
  • 9. Many faces of MVVM • Subjects / Relays as inputs class MessagesViewModel { !// Input let textInput: Observable<String> let sendButton: Observable<Void> let selectItem: Observable<MessageRepresentation> !// Output let dataSource: Observable<[MessageRepresentation]> let isLoading: Observable<Bool> […] }
  • 10. Many faces of MVVM • Subjects / Relays as inputs class MessagesViewModel { !// Input let textInput: Observable<String> let sendButton: Observable<Void> let selectItem: Observable<MessageRepresentation> !// Output let dataSource: Observable<[MessageRepresentation]> let isLoading: Observable<Bool> […] }
  • 11. Many faces of MVVM • Subjects / Relays as inputs class MessagesViewModel { !// Input let textInput: Observable<String> let sendButton: Observable<Void> let selectItem: Observable<MessageRepresentation> !// Output let dataSource: Observable<[MessageRepresentation]> let isLoading: Observable<Bool> […] }
  • 12. Functions as ViewModels • Input and Output per viewModel struct MessagesInput { let textInput: Observable<String> let sendButton: Observable<Void> let selectItem: Observable<MessageRepresentation> } struct MessagesOutput { let dataSource: Observable<[MessageRepresentation]> let isLoading: Observable<Bool> }
  • 13. • From input to output Functions as ViewModels struct MessagesInput { let textInput: Observable<String> let sendButton: Observable<Void> let selectItem: Observable<MessageRepresentation> } struct MessagesOutput { let dataSource: Observable<[MessageRepresentation]> let isLoading: Observable<Bool> }
  • 14. • ViewModelFactory var viewModelFactory: (MessagesInput) !-> MessagesOutput = { _ in fatalError("Missing viewModel factory") } override func viewDidLoad() { super.viewDidLoad() let input = MessagesInput(textInput: textInput.rx.text.unwrap(), sendButton: sendButton.rx.tap.asVoid(), selectItem: .empty()) let output = viewModelFactory(input) bag.insert( output.dataSource.bind(to: dataSource), output.isLoading.bind(to: refreshControl.rx.isRefreshing) ) } Functions as ViewModels struct MessagesInput { let textInput: Observable<String> let sendButton: Observable<Void> let selectItem: Observable<MessageRepresentation> } struct MessagesOutput { let dataSource: Observable<[MessageRepresentation]> let isLoading: Observable<Bool> }
  • 15. • ViewModelFactory var viewModelFactory: (MessagesInput) !-> MessagesOutput = { _ in fatalError("Missing viewModel factory") } override func viewDidLoad() { super.viewDidLoad() let input = MessagesInput(textInput: textInput.rx.text.unwrap(), sendButton: sendButton.rx.tap.asVoid(), selectItem: .empty()) let output = viewModelFactory(input) bag.insert( output.dataSource.bind(to: dataSource), output.isLoading.bind(to: refreshControl.rx.isRefreshing) ) } Functions as ViewModels struct MessagesInput { let textInput: Observable<String> let sendButton: Observable<Void> let selectItem: Observable<MessageRepresentation> } struct MessagesOutput { let dataSource: Observable<[MessageRepresentation]> let isLoading: Observable<Bool> }
  • 16. • ViewModelFactory var viewModelFactory: (MessagesInput) !-> MessagesOutput = { _ in fatalError("Missing viewModel factory") } override func viewDidLoad() { super.viewDidLoad() let input = MessagesInput(textInput: textInput.rx.text.unwrap(), sendButton: sendButton.rx.tap.asVoid(), selectItem: .empty()) let output = viewModelFactory(input) bag.insert( output.dataSource.bind(to: dataSource), output.isLoading.bind(to: refreshControl.rx.isRefreshing) ) } Functions as ViewModels struct MessagesInput { let textInput: Observable<String> let sendButton: Observable<Void> let selectItem: Observable<MessageRepresentation> } struct MessagesOutput { let dataSource: Observable<[MessageRepresentation]> let isLoading: Observable<Bool> }
  • 17. • ViewModelFactory var viewModelFactory: (MessagesInput) !-> MessagesOutput = { _ in fatalError("Missing viewModel factory") } override func viewDidLoad() { super.viewDidLoad() let input = MessagesInput(textInput: textInput.rx.text.unwrap(), sendButton: sendButton.rx.tap.asVoid(), selectItem: .empty()) let output = viewModelFactory(input) bag.insert( output.dataSource.bind(to: dataSource), output.isLoading.bind(to: refreshControl.rx.isRefreshing) ) } Functions as ViewModels struct MessagesInput { let textInput: Observable<String> let sendButton: Observable<Void> let selectItem: Observable<MessageRepresentation> } struct MessagesOutput { let dataSource: Observable<[MessageRepresentation]> let isLoading: Observable<Bool> }
  • 18. • ViewModelFactory var viewModelFactory: (MessagesInput) !-> MessagesOutput = { _ in fatalError("Missing viewModel factory") } override func viewDidLoad() { super.viewDidLoad() let input = MessagesInput(textInput: textInput.rx.text.unwrap(), sendButton: sendButton.rx.tap.asVoid(), selectItem: .empty()) let output = viewModelFactory(input) bag.insert( output.dataSource.bind(to: dataSource), output.isLoading.bind(to: refreshControl.rx.isRefreshing) ) } Functions as ViewModels struct MessagesInput { let textInput: Observable<String> let sendButton: Observable<Void> let selectItem: Observable<MessageRepresentation> } struct MessagesOutput { let dataSource: Observable<[MessageRepresentation]> let isLoading: Observable<Bool> }
  • 19. • The ViewModel itself Functions as ViewModels struct MessagesInput { let textInput: Observable<String> let sendButton: Observable<Void> let selectItem: Observable<MessageRepresentation> } struct MessagesOutput { let dataSource: Observable<[MessageRepresentation]> let isLoading: Observable<Bool> }
  • 20. func messagesViewModel(messageStream: MessageStream) !-> (_ input: MessagesInput) !-> (MessagesOutput) { { (input: MessagesInput) in let messages = messageStream.messages let isLoading = messageStream.isLoading return MessagesOutput(dataSource: messages, isLoading: isLoading) } } Functions as ViewModels • The ViewModel itself struct MessagesInput { let textInput: Observable<String> let sendButton: Observable<Void> let selectItem: Observable<MessageRepresentation> } struct MessagesOutput { let dataSource: Observable<[MessageRepresentation]> let isLoading: Observable<Bool> }
  • 21. func messagesViewModel(messageStream: MessageStream) !-> (_ input: MessagesInput) !-> (MessagesOutput) { { (input: MessagesInput) in let messages = messageStream.messages let isLoading = messageStream.isLoading return MessagesOutput(dataSource: messages, isLoading: isLoading) } } Functions as ViewModels • The ViewModel itself struct MessagesInput { let textInput: Observable<String> let sendButton: Observable<Void> let selectItem: Observable<MessageRepresentation> } struct MessagesOutput { let dataSource: Observable<[MessageRepresentation]> let isLoading: Observable<Bool> }
  • 22. func messagesViewModel(messageStream: MessageStream) !-> (_ input: MessagesInput) !-> (MessagesOutput) { { (input: MessagesInput) in let messages = messageStream.messages let isLoading = messageStream.isLoading return MessagesOutput(dataSource: messages, isLoading: isLoading) } } Functions as ViewModels • The ViewModel itself struct MessagesInput { let textInput: Observable<String> let sendButton: Observable<Void> let selectItem: Observable<MessageRepresentation> } struct MessagesOutput { let dataSource: Observable<[MessageRepresentation]> let isLoading: Observable<Bool> }
  • 23. Functions as ViewModels • Actions / Side effects
  • 24. func messagesViewModel(messageStream: MessageStream) !-> (_ input: MessagesInput) !-> (output: MessagesOutput, actions: Observable<MessagesAction>) { { (input: MessagesInput) in let messages = messageStream.messages.mapMany(MessageRepresentation.init) let isLoading = messageStream.isLoading let output = MessagesOutput(dataSource: messages, isLoading: isLoading) let newMessage = input.sendButton .withLatestFrom(input.textInput) .filter { $0.isEmpty } .map(MessagesAction.newMessage) let displayMessage = input.selectItem.map(MessagesAction.message) let effects = Observable.merge(newMessage, displayMessage) return ( output, effects) } } Functions as ViewModels • Actions / Side effects
  • 25. func messagesViewModel(messageStream: MessageStream) !-> (_ input: MessagesInput) !-> (output: MessagesOutput, actions: Observable<MessagesAction>) { { (input: MessagesInput) in let messages = messageStream.messages.mapMany(MessageRepresentation.init) let isLoading = messageStream.isLoading let output = MessagesOutput(dataSource: messages, isLoading: isLoading) let newMessage = input.sendButton .withLatestFrom(input.textInput) .filter { $0.isEmpty } .map(MessagesAction.newMessage) let displayMessage = input.selectItem.map(MessagesAction.message) let effects = Observable.merge(newMessage, displayMessage) return ( output, effects) } } Functions as ViewModels • Actions / Side effects
  • 26. func messagesViewModel(messageStream: MessageStream) !-> (_ input: MessagesInput) !-> (output: MessagesOutput, actions: Observable<MessagesAction>) { { (input: MessagesInput) in let messages = messageStream.messages.mapMany(MessageRepresentation.init) let isLoading = messageStream.isLoading let output = MessagesOutput(dataSource: messages, isLoading: isLoading) let newMessage = input.sendButton .withLatestFrom(input.textInput) .filter { $0.isEmpty } .map(MessagesAction.newMessage) let displayMessage = input.selectItem.map(MessagesAction.message) let effects = Observable.merge(newMessage, displayMessage) return ( output, effects) } } Functions as ViewModels • Actions / Side effects
  • 27. Everything you need before looking at the implementation
  • 29. Sources • Daniel Tartaglia • https://github.com/danielt1263/RxMyCoordinator • http://reactivex.io • https://medium.com/flawless-app-stories/simplifying- rxswift-code-78071d5b780 • https://github.com/ReactiveX/RxSwift • http://slack.rxswift.org/
  • 31. Injection protocol HasViewModel: class { associatedtype Inputs associatedtype Outputs var viewModelFactory: (Inputs) !-> Outputs { get set } } extension HasViewModel { func installOutputViewModel<Action>(factory: @escaping (Inputs) !-> (Outputs, Observable<Action>)) !-> Observable<Action> { return Observable.create { [weak self] observer in let disposable = CompositeDisposable() self!?.viewModelFactory = { inputs in let vm = factory(inputs) _ = disposable.insert(vm.1.bind(to: observer)) return vm.0 } return disposable }.bindToInterstitials() } }
  • 32. Injection - 2 Coordinator Example: func homeCoordinator(navigationController: UINavigationController?) { let homeViewController = navigationController!?.topViewController as! HomeViewController let actions = homeViewController.installOutputViewModel( factory: homeViewModel(api: Api.network) ) _ = actions.bind(onNext: actionHandler) }