SlideShare una empresa de Scribd logo
1 de 104
Descargar para leer sin conexión
Giovanni Catania
Refactoring ReSwift
Applications
In order to make them modular
About me
• Senior iOS Developer @ PhotoSì

• First talk in Swift Heroes!

• Functional Programming Lover
@gioevi
Gioevi90
PhotoSì
• Leader in Italy in printing photos, photobooks and
photo gift

• Growing reality

• Download the app using the QR code!
Agenda
Agenda
• ReSwift
Agenda
• ReSwift
• Modularization
Agenda
• ReSwift
• Modularization
• Sample Application
Agenda
• ReSwift
• Modularization
• Sample Application
• Modularize the Sample Application: Problems and Solutions
Agenda
• ReSwift
• Modularization
• Sample Application
• Modularize the Sample Application: Problems and Solutions
• Summary & Questions
ReSwift
ReSwift
ReSwift is a small framework that helps you to create Redux-like
architectures in Swift
ReSwift
ReSwift is a small framework that helps you to create Redux-like
architectures in Swift
• View
ReSwift
ReSwift is a small framework that helps you to create Redux-like
architectures in Swift
• View
• Action
ReSwift
ReSwift is a small framework that helps you to create Redux-like
architectures in Swift
• View
• Action
• Reducer
ReSwift
ReSwift is a small framework that helps you to create Redux-like
architectures in Swift
• View
• Action
• Reducer
• Store
Unidirectional Data Flow
Redux: why?
• Unidirectional Data Flow: it’s easier to debug
Redux: why?
• Unidirectional Data Flow: it’s easier to debug
• Code organization: it’s easier to get started
Redux: why?
• Unidirectional Data Flow: it’s easier to debug
• Code organization: it’s easier to get started
• Logic in reducers: it’s easier to test
ReSwift: why?
• The most starred framework to implement a redux architecture
ReSwift: why?
• The most starred framework to implement a redux architecture
• Platform independent
ReSwift: drawbacks
• All the App relies on a global AppState
Modularization
Modularization
Modularization is the activity of dividing a product or a system
into modules that are interchangeable
Modularization
Application
Modularization
Application
Module 1
Module 2
Module 3
Modularization: why?
• Interchangeability
Modularization: why?
• Interchangeability
• Isolation
Modularization: why?
• Interchangeability
• Isolation
• Easy to debug
Modularization: why?
• Interchangeability
• Isolation
• Easy to debug
• Easy to test
Sample app: Kittens 🐈
Sample app: Kittens 🐈
View
struct CatListView: View {
@ObservedObject var viewModel: CatListViewModel
View
Store
Reducer
Action
View
struct CatListView: View {
@ObservedObject var viewModel: CatListViewModel
init(store: Store<AppState>) {
viewModel = CatListViewModel(store: store)
}
View
Store
Reducer
Action
View
struct CatListView: View {
@ObservedObject var viewModel: CatListViewModel
init(store: Store<AppState>) {
viewModel = CatListViewModel(store: store)
}
var body: some View {
// view implementation
View
Store
Reducer
Action
View Model
class CatListViewModel: ObservableObject {
let store: Store<AppState>
@Published var isLoading: Bool
@Published var list: [CatPresentationModel]
init(store: Store<AppState>) {
self.store = store
isLoading = store.state.catListState.loading
list = store.state.catListState.list
}
}
View
Store
Reducer
Action
View Model
extension CatListViewModel {
func fetchList() {
store.dispatch(fetchCats)
}
}
View
Store
Reducer
Action
Actions: Middleware
let catListMiddleware: Middleware<AppState>
= createThunkMiddleware()
View
Store
Reducer
Action
Actions: Middleware
let catListMiddleware: Middleware<AppState>
= createThunkMiddleware()
View
Store
Reducer
Action
let fetchCats = Thunk<CatListState> { dispatch, getState in
dispatch(CatListAction.Fetch())
Task { @MainActor in
let list = await fetchCatList()
.map { $0.compactMap(CatPresentationModel.init) }
switch list {
case let .success(list):
dispatch(CatListAction.SetList(list: list))
...
Reducer
public func catListReducer(action: Action, state: CatListState?) -> CatListState {
var state = state ?? .init(list: [], error: nil, loading: false)
switch action {
case _ as CatListAction.Fetch:
state = .init(list: [], error: nil, loading: true)
case let event as CatListAction.SetList:
state = .init(list: event.list, error: nil, loading: false)
default:
break
}
return state
}
View
Store
Reducer
Action
View Model
extension CatListViewModel: StoreSubscriber {
typealias StoreSubscriberStateType = CatListState
func newState(state: CatListState) {
isLoading = state.loading
list = state.list
}
}
View
Store
Reducer
Action
App
var store = Store<AppState>(reducer: appReducer,
state: nil,
middleware: [catListMiddleware])
@main
struct CatsApp: App {
var body: some Scene {
WindowGroup {
CatListView(store: store)
}
}
}
Let’s start modularizing!
Let’s start modularizing!
1. Create a new Swift Package
Let’s start modularizing!
1. Create a new Swift Package
2. Move all the CatList files inside the new package
Let’s start modularizing!
1. Create a new Swift Package
2. Move all the CatList files inside the new package
3. Build the new package
Let’s start modularizing!
1. Create a new Swift Package
2. Move all the CatList files inside the new package
3. Build the new package
4. Build the main Application
Create a new Swift Package
• Create a new Swift Package called CatList
let package = Package(
name: "CatList"
)
Create a new Swift Package
• Create a new Swift Package called CatList

• Set the Swift package supported platforms
let package = Package(
name: "CatList",
platforms: [.iOS(.v15)],
)
Create a new Swift Package
• Create a new Swift Package called CatList

• Set the Swift package supported platforms

• Add the ReSwift and ReSwiftThunk dependencies
let package = Package(
name: "CatList",
platforms: [.iOS(.v15)],
dependencies: [
.package(name: "ReSwift")
.package(name: "ReSwiftThunk")
],
)
Create a new Swift Package
• Create a new Swift Package called CatList

• Set the Swift package supported platforms

• Add the ReSwift and ReSwiftThunk dependencies 

• Add the Network dependency
let package = Package(
name: "CatList",
platforms: [.iOS(.v15)],
dependencies: [
.package(name: "ReSwift")
.package(name: "ReSwiftThunk"),
.package(name: "Network")
],
)
Move the CatList files in the new Package
• Drag the
fi
les in the new package
Move the CatList files in the new Package
• Drag the
fi
les in the new package
• Add the missing imports where needed
class CatListViewModel: ObservableObject {
let store: Store<AppState>
...
}
Build the new package
class CatListViewModel: ObservableObject {
let store: Store<AppState>
...
}
Cannot
fi
nd type 'AppState' in scope
Build the new package
class CatListViewModel: ObservableObject {
let store: Store<AppState>
...
}
Cannot
fi
nd type 'AppState' in scope
init(store: Store<AppState>) {
viewModel = CatListViewModel(store: store)
}
let catListMiddleware: Middleware<AppState>
= createThunkMiddleware()
let fetchCats = Thunk<AppState> { dispatch, getState in
Build the new package
Use the CatListState 

instead of the AppState!
Build the new package
Middleware<AppState> Middleware<CatListState>
Store<AppState> Store<CatListState>
Build the new package
Middleware<AppState> Middleware<CatListState>
Store<AppState> Store<CatListState>
For sure this will be a problem when we 

will try to build the main application
Build the new package
Build Succeeded!
Build the new package
var store = Store<AppState>(reducer: appReducer,
state: nil,
middleware: [catListMiddleware])
Type of expression is ambiguous
without more context
Build the application: Middleware
f(x) = y
f: Middleware<CatListState> -> Middleware<AppState>
Build the application: Middleware
public typealias DispatchFunction = (Action) -> Void
Build the application: Middleware
public typealias DispatchFunction = (Action) -> Void
public typealias Middleware<State> =
(@escaping DispatchFunction, @escaping () -> State?)
-> (@escaping DispatchFunction) -> DispatchFunction
Build the application: Middleware
public typealias DispatchFunction = (Action) -> Void
public typealias Middleware<State> =
(@escaping DispatchFunction, @escaping () -> State?)
-> (@escaping DispatchFunction) -> DispatchFunction
func function
}
}
) -> Middleware<GlobalState> {
return { globalDispatchFunction, globalStateGetter in
Build the application: Middleware
(input: Middleware<LocalState>
public typealias DispatchFunction = (Action) -> Void
public typealias Middleware<State> =
(@escaping DispatchFunction, @escaping () -> State?)
-> (@escaping DispatchFunction) -> DispatchFunction
func function
}
}
) -> Middleware<GlobalState> {
return { globalDispatchFunction, globalStateGetter in
Build the application: Middleware
(input: Middleware<LocalState>,
state: KeyPath<GlobalState, LocalState>
public typealias DispatchFunction = (Action) -> Void
public typealias Middleware<State> =
(@escaping DispatchFunction, @escaping () -> State?)
-> (@escaping DispatchFunction) -> DispatchFunction
func function
}
}
) -> Middleware<GlobalState> {
return { globalDispatchFunction, globalStateGetter in
Build the application: Middleware
(input: Middleware<LocalState>,
state: KeyPath<GlobalState, LocalState>
return input(globalDispatchFunction, { globalStateGetter()?[keyPath: state] })
public typealias DispatchFunction = (Action) -> Void
public typealias Middleware<State> =
(@escaping DispatchFunction, @escaping () -> State?)
-> (@escaping DispatchFunction) -> DispatchFunction
func pullback
}
}
) -> Middleware<GlobalState> {
return { globalDispatchFunction, globalStateGetter in
Build the application: Middleware
(input: Middleware<LocalState>,
state: KeyPath<GlobalState, LocalState>
return input(globalDispatchFunction, { globalStateGetter()?[keyPath: state] })
var store = Store<AppState>(reducer: appReducer,
state: nil,
middleware: [catListMiddleware])
Build the application: Middleware
var store = Store<AppState>(reducer: appReducer,
state: nil,
middleware: [pullback(input: catListMiddleware,
state: .catListState)])
Build the application: Middleware
@main
struct CatsApp: App {
var body: some Scene {
WindowGroup {
CatListView(store: store)
}
}
}
Cannot convert value of type 'Store<AppState>'
to expected argument type 'Store<CatListState>'
Build the application: Store
f(x) = y
f: Store<AppState> -> Store<CatListState>
Build the application: Store
func function(
typealias Reducer<State> =
(_ action: Action, _ state: State?) -> State
}
}
) -> Store<LocalState> {
Build the application: Store
extension Store {
func function(
typealias Reducer<State> =
(_ action: Action, _ state: State?) -> State
}
}
) -> Store<LocalState> {
Build the application: Store
extension Store {
state: KeyPath<State, LocalState>,
defaultState: LocalState
Store(reducer: { action, localState in
dispatch(action)
return self.state?[keyPath: state] ?? defaultState
}, state: self.state?[keyPath: state])
func function(
typealias Reducer<State> =
(_ action: Action, _ state: State?) -> State
}
}
) -> Store<LocalState> {
Build the application: Store
extension Store {
state: KeyPath<State, LocalState>,
defaultState: LocalState
Store(reducer: { action, localState in
dispatch(action)
return self.state?[keyPath: state] ?? defaultState
}, state: self.state?[keyPath: state])
Build the application: Store
Build the application: Store
This is not the correct way to do it!

The app doesn’t work anymore!
Build the application: Store
LocalStore
Build the application: Store
LocalStore LocalView
Updates
Build the application: Store
LocalStore
LocalReducer
LocalView
Action
Updates
Build the application: Store
LocalStore
LocalReducer
LocalView
Reducer
Action
Action
Updates
Build the application: Store
LocalStore
LocalReducer
LocalView
Store
Reducer
Action
Action Updates
Updates
Build the application: Store
LocalStore
LocalReducer
LocalView
Store
Reducer
Action
Action Updates No-one
listening
here
Updates
Updates
Build the application: Store
LocalStore
LocalReducer
LocalView
Store
Reducer
Action
Action Updates No-one
listening
here
Updates
Updates
Let’s change approach! 

Create a wrapper instead!
Build the application: Store
class LocalStore {
let dispatchFunction: DispatchFunction
let subscribeFunction: (StoreSubscriber) -> Void
let unsubscribeFunction: (AnyStoreSubscriber) -> Void
let getState: () -> State
}
Build the application: Store
public var state: State {
getState() }
public func dispatch(_ action: Action) {
self.dispatchFunction(action) }
public func subscribe(_ subscriber: StoreSubscriber) {
subscribeFunction(subscriber) }
public func unsubscribe(_ subscriber: AnyStoreSubscriber) {
unsubscribeFunction(subscriber) }
func function(
typealias Reducer<State> =
(_ action: Action, _ state: State?) -> State
}
}
) -> LocalStore<LocalState> {
Build the application: Store
extension Store {
LocalStore(getState: { self.state[keyPath: state] },
dispatchFunction: { self.dispatch($0) },
subscribeFunction: { self.subscribe($0) { $0.select { $0[keyPath: state] }} },
unsubscribeFunction: { self.unsubscribe($0) })
func function(
typealias Reducer<State> =
(_ action: Action, _ state: State?) -> State
}
}
) -> LocalStore<LocalState> {
Build the application: Store
extension Store {
state: KeyPath<State, LocalState>
LocalStore(getState: { self.state[keyPath: state] },
dispatchFunction: { self.dispatch($0) },
subscribeFunction: { self.subscribe($0) { $0.select { $0[keyPath: state] }} },
unsubscribeFunction: { self.unsubscribe($0) })
func view(
typealias Reducer<State> =
(_ action: Action, _ state: State?) -> State
}
}
) -> LocalStore<LocalState> {
Build the application: Store
extension Store {
state: KeyPath<State, LocalState>
@main
struct CatsApp: App {
var body: some Scene {
WindowGroup {
CatListView(store: store.view(state: .catListState))
}
}
}
Build the application: Store
Build Succeeded!
Summary
Summary
• ReSwift: implement Redux in Swift
Summary
• ReSwift: implement Redux in Swift
• Modularization: a lot of bene
fi
ts
Summary
• ReSwift: implement Redux in Swift
• Modularization: a lot of bene
fi
ts
• Pullback to handle middlewares
Summary
• ReSwift: implement Redux in Swift
• Modularization: a lot of bene
fi
ts
• LocalStore wrapper implementation
• Pullback to handle middlewares
Summary
• ReSwift: implement Redux in Swift
• Modularization: a lot of bene
fi
ts
• LocalStore wrapper implementation
• Pullback to handle middlewares
• Store view function to assign local store to modules
Links
• Project: https://github.com/Gioevi90/Kittens 

• Point-free: https://www.pointfree.co/collections/composable-architecture
Questions

Más contenido relacionado

La actualidad más candente

Introduction to React Native
Introduction to React NativeIntroduction to React Native
Introduction to React NativeRami Sayar
 
Evan Schultz - Angular Camp - ng2-redux
Evan Schultz - Angular Camp - ng2-reduxEvan Schultz - Angular Camp - ng2-redux
Evan Schultz - Angular Camp - ng2-reduxEvan Schultz
 
Web automation with #d8rules (European Drupal Days 2015)
Web automation with #d8rules (European Drupal Days 2015)Web automation with #d8rules (European Drupal Days 2015)
Web automation with #d8rules (European Drupal Days 2015)Eugenio Minardi
 
Building Large Scale PHP Web Applications with Laravel 4
Building Large Scale PHP Web Applications with Laravel 4Building Large Scale PHP Web Applications with Laravel 4
Building Large Scale PHP Web Applications with Laravel 4Darwin Biler
 
Get rid of controllers in angular 1.5.x start using component directives
Get rid of controllers in angular 1.5.x start using component directivesGet rid of controllers in angular 1.5.x start using component directives
Get rid of controllers in angular 1.5.x start using component directivesMarios Fakiolas
 
Reactive.architecture.with.Angular
Reactive.architecture.with.AngularReactive.architecture.with.Angular
Reactive.architecture.with.AngularEvan Schultz
 
Introduction to ReactJS and Redux
Introduction to ReactJS and ReduxIntroduction to ReactJS and Redux
Introduction to ReactJS and ReduxBoris Dinkevich
 
API Development with Laravel
API Development with LaravelAPI Development with Laravel
API Development with LaravelMichael Peacock
 
How to Implement Basic Angular Routing and Nested Routing With Params in Angu...
How to Implement Basic Angular Routing and Nested Routing With Params in Angu...How to Implement Basic Angular Routing and Nested Routing With Params in Angu...
How to Implement Basic Angular Routing and Nested Routing With Params in Angu...Katy Slemon
 
AI: Integrate Search Function into Your App Using Bing Search API.
AI: Integrate Search Function into Your App Using Bing Search API.AI: Integrate Search Function into Your App Using Bing Search API.
AI: Integrate Search Function into Your App Using Bing Search API.Marvin Heng
 
Spring MVC Intro / Gore - Nov NHJUG
Spring MVC Intro / Gore - Nov NHJUGSpring MVC Intro / Gore - Nov NHJUG
Spring MVC Intro / Gore - Nov NHJUGTed Pennings
 
2011 a grape odyssey
2011   a grape odyssey2011   a grape odyssey
2011 a grape odysseyMike Hagedorn
 
SwiftUI - Performance and Memory Management
SwiftUI - Performance and Memory ManagementSwiftUI - Performance and Memory Management
SwiftUI - Performance and Memory ManagementWannitaTolaema
 
MySQL Software Repositories
MySQL Software RepositoriesMySQL Software Repositories
MySQL Software RepositoriesAkhil Mohan
 
What's New In Laravel 5
What's New In Laravel 5What's New In Laravel 5
What's New In Laravel 5Darren Craig
 
Laravel Starter Kit | Laravel Admin Template-ChandraAdmin
Laravel Starter Kit | Laravel Admin Template-ChandraAdmin Laravel Starter Kit | Laravel Admin Template-ChandraAdmin
Laravel Starter Kit | Laravel Admin Template-ChandraAdmin Lorvent56
 
What's New in Laravel 5 (Laravel Meetup - 23th Apr 15, Yogyakarta, ID)
What's New in Laravel 5 (Laravel Meetup - 23th Apr 15, Yogyakarta, ID)What's New in Laravel 5 (Laravel Meetup - 23th Apr 15, Yogyakarta, ID)
What's New in Laravel 5 (Laravel Meetup - 23th Apr 15, Yogyakarta, ID)Roes Wibowo
 
iOS viper presentation
iOS viper presentationiOS viper presentation
iOS viper presentationRajat Datta
 
Web Development with Laravel 5
Web Development with Laravel 5Web Development with Laravel 5
Web Development with Laravel 5Soheil Khodayari
 

La actualidad más candente (20)

Introduction to React Native
Introduction to React NativeIntroduction to React Native
Introduction to React Native
 
Evan Schultz - Angular Camp - ng2-redux
Evan Schultz - Angular Camp - ng2-reduxEvan Schultz - Angular Camp - ng2-redux
Evan Schultz - Angular Camp - ng2-redux
 
How to React Native
How to React NativeHow to React Native
How to React Native
 
Web automation with #d8rules (European Drupal Days 2015)
Web automation with #d8rules (European Drupal Days 2015)Web automation with #d8rules (European Drupal Days 2015)
Web automation with #d8rules (European Drupal Days 2015)
 
Building Large Scale PHP Web Applications with Laravel 4
Building Large Scale PHP Web Applications with Laravel 4Building Large Scale PHP Web Applications with Laravel 4
Building Large Scale PHP Web Applications with Laravel 4
 
Get rid of controllers in angular 1.5.x start using component directives
Get rid of controllers in angular 1.5.x start using component directivesGet rid of controllers in angular 1.5.x start using component directives
Get rid of controllers in angular 1.5.x start using component directives
 
Reactive.architecture.with.Angular
Reactive.architecture.with.AngularReactive.architecture.with.Angular
Reactive.architecture.with.Angular
 
Introduction to ReactJS and Redux
Introduction to ReactJS and ReduxIntroduction to ReactJS and Redux
Introduction to ReactJS and Redux
 
API Development with Laravel
API Development with LaravelAPI Development with Laravel
API Development with Laravel
 
How to Implement Basic Angular Routing and Nested Routing With Params in Angu...
How to Implement Basic Angular Routing and Nested Routing With Params in Angu...How to Implement Basic Angular Routing and Nested Routing With Params in Angu...
How to Implement Basic Angular Routing and Nested Routing With Params in Angu...
 
AI: Integrate Search Function into Your App Using Bing Search API.
AI: Integrate Search Function into Your App Using Bing Search API.AI: Integrate Search Function into Your App Using Bing Search API.
AI: Integrate Search Function into Your App Using Bing Search API.
 
Spring MVC Intro / Gore - Nov NHJUG
Spring MVC Intro / Gore - Nov NHJUGSpring MVC Intro / Gore - Nov NHJUG
Spring MVC Intro / Gore - Nov NHJUG
 
2011 a grape odyssey
2011   a grape odyssey2011   a grape odyssey
2011 a grape odyssey
 
SwiftUI - Performance and Memory Management
SwiftUI - Performance and Memory ManagementSwiftUI - Performance and Memory Management
SwiftUI - Performance and Memory Management
 
MySQL Software Repositories
MySQL Software RepositoriesMySQL Software Repositories
MySQL Software Repositories
 
What's New In Laravel 5
What's New In Laravel 5What's New In Laravel 5
What's New In Laravel 5
 
Laravel Starter Kit | Laravel Admin Template-ChandraAdmin
Laravel Starter Kit | Laravel Admin Template-ChandraAdmin Laravel Starter Kit | Laravel Admin Template-ChandraAdmin
Laravel Starter Kit | Laravel Admin Template-ChandraAdmin
 
What's New in Laravel 5 (Laravel Meetup - 23th Apr 15, Yogyakarta, ID)
What's New in Laravel 5 (Laravel Meetup - 23th Apr 15, Yogyakarta, ID)What's New in Laravel 5 (Laravel Meetup - 23th Apr 15, Yogyakarta, ID)
What's New in Laravel 5 (Laravel Meetup - 23th Apr 15, Yogyakarta, ID)
 
iOS viper presentation
iOS viper presentationiOS viper presentation
iOS viper presentation
 
Web Development with Laravel 5
Web Development with Laravel 5Web Development with Laravel 5
Web Development with Laravel 5
 

Similar a Refactoring ReSwift Applications in order to make them modular

A tour through Swift attributes
A tour through Swift attributesA tour through Swift attributes
A tour through Swift attributesMarco Eidinger
 
Baruco 2014 - Rubymotion Workshop
Baruco 2014 - Rubymotion WorkshopBaruco 2014 - Rubymotion Workshop
Baruco 2014 - Rubymotion WorkshopBrian Sam-Bodden
 
Android Workshop
Android WorkshopAndroid Workshop
Android WorkshopJunda Ong
 
Advanced #6 clean architecture
Advanced #6  clean architectureAdvanced #6  clean architecture
Advanced #6 clean architectureVitali Pekelis
 
Refactoring Large Web Applications with Backbone.js
Refactoring Large Web Applications with Backbone.jsRefactoring Large Web Applications with Backbone.js
Refactoring Large Web Applications with Backbone.jsStacy London
 
Refactor Large applications with Backbone
Refactor Large applications with BackboneRefactor Large applications with Backbone
Refactor Large applications with BackboneColdFusionConference
 
Refactor Large apps with Backbone
Refactor Large apps with BackboneRefactor Large apps with Backbone
Refactor Large apps with BackbonedevObjective
 
FrenchKit: SwiftUI Data Flow with Redux
FrenchKit: SwiftUI Data Flow with ReduxFrenchKit: SwiftUI Data Flow with Redux
FrenchKit: SwiftUI Data Flow with ReduxThomasRicouard
 
How To Integrate Native Android App With React Native.
How To Integrate Native Android App With React Native.How To Integrate Native Android App With React Native.
How To Integrate Native Android App With React Native.Techugo
 
Play Support in Cloud Foundry
Play Support in Cloud FoundryPlay Support in Cloud Foundry
Play Support in Cloud Foundryrajdeep
 
Modern JavaScript, without giving up on Rails
Modern JavaScript, without giving up on RailsModern JavaScript, without giving up on Rails
Modern JavaScript, without giving up on RailsJonathan Johnson
 
Testing Big in JavaScript
Testing Big in JavaScriptTesting Big in JavaScript
Testing Big in JavaScriptRobert DeLuca
 
WordPress Developers Israel Meetup #1
WordPress Developers Israel Meetup #1WordPress Developers Israel Meetup #1
WordPress Developers Israel Meetup #1Yoav Farhi
 
From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)Jose Manuel Pereira Garcia
 
Moderne App-Architektur mit Dagger2 und RxJava
Moderne App-Architektur mit Dagger2 und RxJavaModerne App-Architektur mit Dagger2 und RxJava
Moderne App-Architektur mit Dagger2 und RxJavainovex GmbH
 
Migration from vaadin 6 to vaadin 7 devoxx france 2013
Migration from vaadin 6 to vaadin 7   devoxx france 2013Migration from vaadin 6 to vaadin 7   devoxx france 2013
Migration from vaadin 6 to vaadin 7 devoxx france 2013Joonas Lehtinen
 
React && React Native workshop
React && React Native workshopReact && React Native workshop
React && React Native workshopStacy Goh
 
Dependency injection in Java, from naive to functional
Dependency injection in Java, from naive to functionalDependency injection in Java, from naive to functional
Dependency injection in Java, from naive to functionalMarian Wamsiedel
 
Quick Start to iOS Development
Quick Start to iOS DevelopmentQuick Start to iOS Development
Quick Start to iOS DevelopmentJussi Pohjolainen
 
OpenWhisk Under the Hood -- London Oct 16 2016
OpenWhisk Under the Hood -- London Oct 16 2016OpenWhisk Under the Hood -- London Oct 16 2016
OpenWhisk Under the Hood -- London Oct 16 2016Stephen Fink
 

Similar a Refactoring ReSwift Applications in order to make them modular (20)

A tour through Swift attributes
A tour through Swift attributesA tour through Swift attributes
A tour through Swift attributes
 
Baruco 2014 - Rubymotion Workshop
Baruco 2014 - Rubymotion WorkshopBaruco 2014 - Rubymotion Workshop
Baruco 2014 - Rubymotion Workshop
 
Android Workshop
Android WorkshopAndroid Workshop
Android Workshop
 
Advanced #6 clean architecture
Advanced #6  clean architectureAdvanced #6  clean architecture
Advanced #6 clean architecture
 
Refactoring Large Web Applications with Backbone.js
Refactoring Large Web Applications with Backbone.jsRefactoring Large Web Applications with Backbone.js
Refactoring Large Web Applications with Backbone.js
 
Refactor Large applications with Backbone
Refactor Large applications with BackboneRefactor Large applications with Backbone
Refactor Large applications with Backbone
 
Refactor Large apps with Backbone
Refactor Large apps with BackboneRefactor Large apps with Backbone
Refactor Large apps with Backbone
 
FrenchKit: SwiftUI Data Flow with Redux
FrenchKit: SwiftUI Data Flow with ReduxFrenchKit: SwiftUI Data Flow with Redux
FrenchKit: SwiftUI Data Flow with Redux
 
How To Integrate Native Android App With React Native.
How To Integrate Native Android App With React Native.How To Integrate Native Android App With React Native.
How To Integrate Native Android App With React Native.
 
Play Support in Cloud Foundry
Play Support in Cloud FoundryPlay Support in Cloud Foundry
Play Support in Cloud Foundry
 
Modern JavaScript, without giving up on Rails
Modern JavaScript, without giving up on RailsModern JavaScript, without giving up on Rails
Modern JavaScript, without giving up on Rails
 
Testing Big in JavaScript
Testing Big in JavaScriptTesting Big in JavaScript
Testing Big in JavaScript
 
WordPress Developers Israel Meetup #1
WordPress Developers Israel Meetup #1WordPress Developers Israel Meetup #1
WordPress Developers Israel Meetup #1
 
From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)
 
Moderne App-Architektur mit Dagger2 und RxJava
Moderne App-Architektur mit Dagger2 und RxJavaModerne App-Architektur mit Dagger2 und RxJava
Moderne App-Architektur mit Dagger2 und RxJava
 
Migration from vaadin 6 to vaadin 7 devoxx france 2013
Migration from vaadin 6 to vaadin 7   devoxx france 2013Migration from vaadin 6 to vaadin 7   devoxx france 2013
Migration from vaadin 6 to vaadin 7 devoxx france 2013
 
React && React Native workshop
React && React Native workshopReact && React Native workshop
React && React Native workshop
 
Dependency injection in Java, from naive to functional
Dependency injection in Java, from naive to functionalDependency injection in Java, from naive to functional
Dependency injection in Java, from naive to functional
 
Quick Start to iOS Development
Quick Start to iOS DevelopmentQuick Start to iOS Development
Quick Start to iOS Development
 
OpenWhisk Under the Hood -- London Oct 16 2016
OpenWhisk Under the Hood -- London Oct 16 2016OpenWhisk Under the Hood -- London Oct 16 2016
OpenWhisk Under the Hood -- London Oct 16 2016
 

Último

Mobile Application Development-Components and Layouts
Mobile Application Development-Components and LayoutsMobile Application Development-Components and Layouts
Mobile Application Development-Components and LayoutsChandrakantDivate1
 
Mobile Application Development-Android and It’s Tools
Mobile Application Development-Android and It’s ToolsMobile Application Development-Android and It’s Tools
Mobile Application Development-Android and It’s ToolsChandrakantDivate1
 
Leading Mobile App Development Companies in India (2).pdf
Leading Mobile App Development Companies in India (2).pdfLeading Mobile App Development Companies in India (2).pdf
Leading Mobile App Development Companies in India (2).pdfCWS Technology
 
Satara Call girl escort *74796//13122* Call me punam call girls 24*7hour avai...
Satara Call girl escort *74796//13122* Call me punam call girls 24*7hour avai...Satara Call girl escort *74796//13122* Call me punam call girls 24*7hour avai...
Satara Call girl escort *74796//13122* Call me punam call girls 24*7hour avai...nishasame66
 
Android Application Components with Implementation & Examples
Android Application Components with Implementation & ExamplesAndroid Application Components with Implementation & Examples
Android Application Components with Implementation & ExamplesChandrakantDivate1
 

Último (6)

Mobile Application Development-Components and Layouts
Mobile Application Development-Components and LayoutsMobile Application Development-Components and Layouts
Mobile Application Development-Components and Layouts
 
Mobile Application Development-Android and It’s Tools
Mobile Application Development-Android and It’s ToolsMobile Application Development-Android and It’s Tools
Mobile Application Development-Android and It’s Tools
 
Leading Mobile App Development Companies in India (2).pdf
Leading Mobile App Development Companies in India (2).pdfLeading Mobile App Development Companies in India (2).pdf
Leading Mobile App Development Companies in India (2).pdf
 
Satara Call girl escort *74796//13122* Call me punam call girls 24*7hour avai...
Satara Call girl escort *74796//13122* Call me punam call girls 24*7hour avai...Satara Call girl escort *74796//13122* Call me punam call girls 24*7hour avai...
Satara Call girl escort *74796//13122* Call me punam call girls 24*7hour avai...
 
Android Application Components with Implementation & Examples
Android Application Components with Implementation & ExamplesAndroid Application Components with Implementation & Examples
Android Application Components with Implementation & Examples
 
Obat Penggugur Kandungan Di Apotik Kimia Farma (087776558899)
Obat Penggugur Kandungan Di Apotik Kimia Farma (087776558899)Obat Penggugur Kandungan Di Apotik Kimia Farma (087776558899)
Obat Penggugur Kandungan Di Apotik Kimia Farma (087776558899)
 

Refactoring ReSwift Applications in order to make them modular

  • 2. About me • Senior iOS Developer @ PhotoSì • First talk in Swift Heroes! • Functional Programming Lover @gioevi Gioevi90
  • 3. PhotoSì • Leader in Italy in printing photos, photobooks and photo gift • Growing reality • Download the app using the QR code!
  • 8. Agenda • ReSwift • Modularization • Sample Application • Modularize the Sample Application: Problems and Solutions
  • 9. Agenda • ReSwift • Modularization • Sample Application • Modularize the Sample Application: Problems and Solutions • Summary & Questions
  • 11. ReSwift ReSwift is a small framework that helps you to create Redux-like architectures in Swift
  • 12. ReSwift ReSwift is a small framework that helps you to create Redux-like architectures in Swift • View
  • 13. ReSwift ReSwift is a small framework that helps you to create Redux-like architectures in Swift • View • Action
  • 14. ReSwift ReSwift is a small framework that helps you to create Redux-like architectures in Swift • View • Action • Reducer
  • 15. ReSwift ReSwift is a small framework that helps you to create Redux-like architectures in Swift • View • Action • Reducer • Store
  • 16.
  • 18. Redux: why? • Unidirectional Data Flow: it’s easier to debug
  • 19. Redux: why? • Unidirectional Data Flow: it’s easier to debug • Code organization: it’s easier to get started
  • 20. Redux: why? • Unidirectional Data Flow: it’s easier to debug • Code organization: it’s easier to get started • Logic in reducers: it’s easier to test
  • 21. ReSwift: why? • The most starred framework to implement a redux architecture
  • 22. ReSwift: why? • The most starred framework to implement a redux architecture • Platform independent
  • 23. ReSwift: drawbacks • All the App relies on a global AppState
  • 25. Modularization Modularization is the activity of dividing a product or a system into modules that are interchangeable
  • 30. Modularization: why? • Interchangeability • Isolation • Easy to debug
  • 31. Modularization: why? • Interchangeability • Isolation • Easy to debug • Easy to test
  • 34. View struct CatListView: View { @ObservedObject var viewModel: CatListViewModel View Store Reducer Action
  • 35. View struct CatListView: View { @ObservedObject var viewModel: CatListViewModel init(store: Store<AppState>) { viewModel = CatListViewModel(store: store) } View Store Reducer Action
  • 36. View struct CatListView: View { @ObservedObject var viewModel: CatListViewModel init(store: Store<AppState>) { viewModel = CatListViewModel(store: store) } var body: some View { // view implementation View Store Reducer Action
  • 37. View Model class CatListViewModel: ObservableObject { let store: Store<AppState> @Published var isLoading: Bool @Published var list: [CatPresentationModel] init(store: Store<AppState>) { self.store = store isLoading = store.state.catListState.loading list = store.state.catListState.list } } View Store Reducer Action
  • 38. View Model extension CatListViewModel { func fetchList() { store.dispatch(fetchCats) } } View Store Reducer Action
  • 39. Actions: Middleware let catListMiddleware: Middleware<AppState> = createThunkMiddleware() View Store Reducer Action
  • 40. Actions: Middleware let catListMiddleware: Middleware<AppState> = createThunkMiddleware() View Store Reducer Action let fetchCats = Thunk<CatListState> { dispatch, getState in dispatch(CatListAction.Fetch()) Task { @MainActor in let list = await fetchCatList() .map { $0.compactMap(CatPresentationModel.init) } switch list { case let .success(list): dispatch(CatListAction.SetList(list: list)) ...
  • 41. Reducer public func catListReducer(action: Action, state: CatListState?) -> CatListState { var state = state ?? .init(list: [], error: nil, loading: false) switch action { case _ as CatListAction.Fetch: state = .init(list: [], error: nil, loading: true) case let event as CatListAction.SetList: state = .init(list: event.list, error: nil, loading: false) default: break } return state } View Store Reducer Action
  • 42. View Model extension CatListViewModel: StoreSubscriber { typealias StoreSubscriberStateType = CatListState func newState(state: CatListState) { isLoading = state.loading list = state.list } } View Store Reducer Action
  • 43. App var store = Store<AppState>(reducer: appReducer, state: nil, middleware: [catListMiddleware]) @main struct CatsApp: App { var body: some Scene { WindowGroup { CatListView(store: store) } } }
  • 45. Let’s start modularizing! 1. Create a new Swift Package
  • 46. Let’s start modularizing! 1. Create a new Swift Package 2. Move all the CatList files inside the new package
  • 47. Let’s start modularizing! 1. Create a new Swift Package 2. Move all the CatList files inside the new package 3. Build the new package
  • 48. Let’s start modularizing! 1. Create a new Swift Package 2. Move all the CatList files inside the new package 3. Build the new package 4. Build the main Application
  • 49. Create a new Swift Package • Create a new Swift Package called CatList let package = Package( name: "CatList" )
  • 50. Create a new Swift Package • Create a new Swift Package called CatList • Set the Swift package supported platforms let package = Package( name: "CatList", platforms: [.iOS(.v15)], )
  • 51. Create a new Swift Package • Create a new Swift Package called CatList • Set the Swift package supported platforms • Add the ReSwift and ReSwiftThunk dependencies let package = Package( name: "CatList", platforms: [.iOS(.v15)], dependencies: [ .package(name: "ReSwift") .package(name: "ReSwiftThunk") ], )
  • 52. Create a new Swift Package • Create a new Swift Package called CatList • Set the Swift package supported platforms • Add the ReSwift and ReSwiftThunk dependencies • Add the Network dependency let package = Package( name: "CatList", platforms: [.iOS(.v15)], dependencies: [ .package(name: "ReSwift") .package(name: "ReSwiftThunk"), .package(name: "Network") ], )
  • 53. Move the CatList files in the new Package • Drag the fi les in the new package
  • 54. Move the CatList files in the new Package • Drag the fi les in the new package • Add the missing imports where needed
  • 55. class CatListViewModel: ObservableObject { let store: Store<AppState> ... } Build the new package
  • 56. class CatListViewModel: ObservableObject { let store: Store<AppState> ... } Cannot fi nd type 'AppState' in scope Build the new package
  • 57. class CatListViewModel: ObservableObject { let store: Store<AppState> ... } Cannot fi nd type 'AppState' in scope init(store: Store<AppState>) { viewModel = CatListViewModel(store: store) } let catListMiddleware: Middleware<AppState> = createThunkMiddleware() let fetchCats = Thunk<AppState> { dispatch, getState in Build the new package
  • 58.
  • 59. Use the CatListState 
 instead of the AppState!
  • 60. Build the new package Middleware<AppState> Middleware<CatListState> Store<AppState> Store<CatListState>
  • 61. Build the new package Middleware<AppState> Middleware<CatListState> Store<AppState> Store<CatListState> For sure this will be a problem when we will try to build the main application
  • 62. Build the new package
  • 64. var store = Store<AppState>(reducer: appReducer, state: nil, middleware: [catListMiddleware]) Type of expression is ambiguous without more context Build the application: Middleware
  • 65. f(x) = y f: Middleware<CatListState> -> Middleware<AppState> Build the application: Middleware
  • 66. public typealias DispatchFunction = (Action) -> Void Build the application: Middleware
  • 67. public typealias DispatchFunction = (Action) -> Void public typealias Middleware<State> = (@escaping DispatchFunction, @escaping () -> State?) -> (@escaping DispatchFunction) -> DispatchFunction Build the application: Middleware
  • 68. public typealias DispatchFunction = (Action) -> Void public typealias Middleware<State> = (@escaping DispatchFunction, @escaping () -> State?) -> (@escaping DispatchFunction) -> DispatchFunction func function } } ) -> Middleware<GlobalState> { return { globalDispatchFunction, globalStateGetter in Build the application: Middleware (input: Middleware<LocalState>
  • 69. public typealias DispatchFunction = (Action) -> Void public typealias Middleware<State> = (@escaping DispatchFunction, @escaping () -> State?) -> (@escaping DispatchFunction) -> DispatchFunction func function } } ) -> Middleware<GlobalState> { return { globalDispatchFunction, globalStateGetter in Build the application: Middleware (input: Middleware<LocalState>, state: KeyPath<GlobalState, LocalState>
  • 70. public typealias DispatchFunction = (Action) -> Void public typealias Middleware<State> = (@escaping DispatchFunction, @escaping () -> State?) -> (@escaping DispatchFunction) -> DispatchFunction func function } } ) -> Middleware<GlobalState> { return { globalDispatchFunction, globalStateGetter in Build the application: Middleware (input: Middleware<LocalState>, state: KeyPath<GlobalState, LocalState> return input(globalDispatchFunction, { globalStateGetter()?[keyPath: state] })
  • 71. public typealias DispatchFunction = (Action) -> Void public typealias Middleware<State> = (@escaping DispatchFunction, @escaping () -> State?) -> (@escaping DispatchFunction) -> DispatchFunction func pullback } } ) -> Middleware<GlobalState> { return { globalDispatchFunction, globalStateGetter in Build the application: Middleware (input: Middleware<LocalState>, state: KeyPath<GlobalState, LocalState> return input(globalDispatchFunction, { globalStateGetter()?[keyPath: state] })
  • 72. var store = Store<AppState>(reducer: appReducer, state: nil, middleware: [catListMiddleware]) Build the application: Middleware
  • 73. var store = Store<AppState>(reducer: appReducer, state: nil, middleware: [pullback(input: catListMiddleware, state: .catListState)]) Build the application: Middleware
  • 74. @main struct CatsApp: App { var body: some Scene { WindowGroup { CatListView(store: store) } } } Cannot convert value of type 'Store<AppState>' to expected argument type 'Store<CatListState>' Build the application: Store
  • 75. f(x) = y f: Store<AppState> -> Store<CatListState> Build the application: Store
  • 76. func function( typealias Reducer<State> = (_ action: Action, _ state: State?) -> State } } ) -> Store<LocalState> { Build the application: Store extension Store {
  • 77. func function( typealias Reducer<State> = (_ action: Action, _ state: State?) -> State } } ) -> Store<LocalState> { Build the application: Store extension Store { state: KeyPath<State, LocalState>, defaultState: LocalState Store(reducer: { action, localState in dispatch(action) return self.state?[keyPath: state] ?? defaultState }, state: self.state?[keyPath: state])
  • 78. func function( typealias Reducer<State> = (_ action: Action, _ state: State?) -> State } } ) -> Store<LocalState> { Build the application: Store extension Store { state: KeyPath<State, LocalState>, defaultState: LocalState Store(reducer: { action, localState in dispatch(action) return self.state?[keyPath: state] ?? defaultState }, state: self.state?[keyPath: state])
  • 80. Build the application: Store This is not the correct way to do it!
 The app doesn’t work anymore!
  • 81. Build the application: Store LocalStore
  • 82. Build the application: Store LocalStore LocalView Updates
  • 83. Build the application: Store LocalStore LocalReducer LocalView Action Updates
  • 84. Build the application: Store LocalStore LocalReducer LocalView Reducer Action Action Updates
  • 85. Build the application: Store LocalStore LocalReducer LocalView Store Reducer Action Action Updates Updates
  • 86. Build the application: Store LocalStore LocalReducer LocalView Store Reducer Action Action Updates No-one listening here Updates Updates
  • 87. Build the application: Store LocalStore LocalReducer LocalView Store Reducer Action Action Updates No-one listening here Updates Updates Let’s change approach! Create a wrapper instead!
  • 88. Build the application: Store class LocalStore { let dispatchFunction: DispatchFunction let subscribeFunction: (StoreSubscriber) -> Void let unsubscribeFunction: (AnyStoreSubscriber) -> Void let getState: () -> State }
  • 89. Build the application: Store public var state: State { getState() } public func dispatch(_ action: Action) { self.dispatchFunction(action) } public func subscribe(_ subscriber: StoreSubscriber) { subscribeFunction(subscriber) } public func unsubscribe(_ subscriber: AnyStoreSubscriber) { unsubscribeFunction(subscriber) }
  • 90. func function( typealias Reducer<State> = (_ action: Action, _ state: State?) -> State } } ) -> LocalStore<LocalState> { Build the application: Store extension Store {
  • 91. LocalStore(getState: { self.state[keyPath: state] }, dispatchFunction: { self.dispatch($0) }, subscribeFunction: { self.subscribe($0) { $0.select { $0[keyPath: state] }} }, unsubscribeFunction: { self.unsubscribe($0) }) func function( typealias Reducer<State> = (_ action: Action, _ state: State?) -> State } } ) -> LocalStore<LocalState> { Build the application: Store extension Store { state: KeyPath<State, LocalState>
  • 92. LocalStore(getState: { self.state[keyPath: state] }, dispatchFunction: { self.dispatch($0) }, subscribeFunction: { self.subscribe($0) { $0.select { $0[keyPath: state] }} }, unsubscribeFunction: { self.unsubscribe($0) }) func view( typealias Reducer<State> = (_ action: Action, _ state: State?) -> State } } ) -> LocalStore<LocalState> { Build the application: Store extension Store { state: KeyPath<State, LocalState>
  • 93. @main struct CatsApp: App { var body: some Scene { WindowGroup { CatListView(store: store.view(state: .catListState)) } } } Build the application: Store
  • 94.
  • 96.
  • 99. Summary • ReSwift: implement Redux in Swift • Modularization: a lot of bene fi ts
  • 100. Summary • ReSwift: implement Redux in Swift • Modularization: a lot of bene fi ts • Pullback to handle middlewares
  • 101. Summary • ReSwift: implement Redux in Swift • Modularization: a lot of bene fi ts • LocalStore wrapper implementation • Pullback to handle middlewares
  • 102. Summary • ReSwift: implement Redux in Swift • Modularization: a lot of bene fi ts • LocalStore wrapper implementation • Pullback to handle middlewares • Store view function to assign local store to modules
  • 103. Links • Project: https://github.com/Gioevi90/Kittens • Point-free: https://www.pointfree.co/collections/composable-architecture