Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

State composition + Failable reducer + Compact features #70

Merged
merged 114 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
114 commits
Select commit Hold shift + click to select a range
d12b43a
Create PlayersState
stephtelolahy Jun 28, 2024
8263188
Rename
stephtelolahy Jun 28, 2024
4d2e149
handLimit
stephtelolahy Jun 28, 2024
5130113
refactor: use PlayersState
stephtelolahy Jun 29, 2024
4beb46c
refactor: introduce cardLocations state
stephtelolahy Jun 29, 2024
10fe65b
Rewrite drawHand
stephtelolahy Jun 29, 2024
5a3e80d
Compose reducer
stephtelolahy Jun 29, 2024
98996a4
CardLocation
stephtelolahy Jun 29, 2024
8f9d24d
Fix tests
stephtelolahy Jun 29, 2024
8da2f29
Equip
stephtelolahy Jun 30, 2024
57f823b
PutBack
stephtelolahy Jun 30, 2024
123487a
Remove maxHealth
stephtelolahy Jun 30, 2024
6d65565
actionn draw deck
stephtelolahy Jul 2, 2024
2468a5c
drawArena
stephtelolahy Jul 3, 2024
c7cd757
drawDiscardReducer
stephtelolahy Jul 3, 2024
e527445
passInPlay
stephtelolahy Jul 3, 2024
33d2c9c
draw
stephtelolahy Jul 3, 2024
8347e33
Cleanup Player
stephtelolahy Jul 3, 2024
5a51b45
setAttribute
stephtelolahy Jul 3, 2024
88d2b09
Rename CardLocation Field
stephtelolahy Jul 3, 2024
56d8283
typealias PlayersState
stephtelolahy Jul 3, 2024
9c8c3bd
Cleanup
stephtelolahy Jul 3, 2024
0bccfd7
Run tests
stephtelolahy Jul 3, 2024
4ea59a9
Working :)
stephtelolahy Jul 4, 2024
1e723e0
Add round
stephtelolahy Jul 5, 2024
d4350be
Tests
stephtelolahy Jul 5, 2024
282f735
Add SequenceState
stephtelolahy Jul 6, 2024
e6ab15f
activate
stephtelolahy Jul 11, 2024
4b0e18c
ChooseOne
stephtelolahy Jul 11, 2024
4e9028e
Use sequence state
stephtelolahy Jul 11, 2024
fd69f8b
Move gameOver
stephtelolahy Jul 11, 2024
fdcab1b
Cleanup
stephtelolahy Jul 11, 2024
ef8ddb0
ConfigState
stephtelolahy Jul 11, 2024
1bbf70e
Remove event and error from state
stephtelolahy Jul 11, 2024
6ffbcf8
Replace game state reducer
stephtelolahy Jul 11, 2024
821e0c4
Clean
stephtelolahy Jul 11, 2024
d0e3638
CardsState
stephtelolahy Jul 13, 2024
90190f5
Rearrange
stephtelolahy Jul 13, 2024
ab075e3
Cleanup
stephtelolahy Jul 13, 2024
f6c3557
wip
stephtelolahy Jul 13, 2024
942142b
Update image
stephtelolahy Jul 13, 2024
a815d51
Associate error to SubState
stephtelolahy Jul 13, 2024
1a2e6ee
Reset other modules
stephtelolahy Jul 13, 2024
fa3d49f
Fix tests
stephtelolahy Jul 13, 2024
256a17f
Use SelectorReducer
stephtelolahy Jul 13, 2024
d4fd2af
Play reducer
stephtelolahy Jul 13, 2024
a59f40d
wip
stephtelolahy Jul 13, 2024
ed41918
choose reducer
stephtelolahy Jul 13, 2024
d7736f6
Cleanup
stephtelolahy Jul 13, 2024
d185d39
resolve effect
stephtelolahy Jul 13, 2024
0ac4d1c
remove action cancel
stephtelolahy Jul 13, 2024
998610d
Remove cancel actions
stephtelolahy Jul 14, 2024
a9d04ca
Cleanup
stephtelolahy Jul 14, 2024
8e6508e
Restructure
stephtelolahy Jul 14, 2024
4e6a24e
Rearrange
stephtelolahy Jul 14, 2024
f461e9c
Clean
stephtelolahy Jul 14, 2024
240d7fc
EffectOutput
stephtelolahy Jul 14, 2024
58235e6
rename
stephtelolahy Jul 14, 2024
80180cd
Fix CardsRepository tests
stephtelolahy Jul 14, 2024
2f07748
EffectContext remove unused property
stephtelolahy Jul 19, 2024
447c6fc
extension getOrEmpty
stephtelolahy Jul 19, 2024
f92359b
Fix tests
stephtelolahy Jul 19, 2024
a98219b
Add group action
stephtelolahy Jul 19, 2024
539bf89
Refactor EffectOutput replace(_ index: Int, with: GameAction)
stephtelolahy Jul 19, 2024
9f86bcd
Fix force
stephtelolahy Jul 19, 2024
e13aff5
force
stephtelolahy Jul 19, 2024
9ecd2ac
Everything is all right
stephtelolahy Jul 19, 2024
1a5eae4
Cleanup
stephtelolahy Jul 19, 2024
fcd621c
Settings state
stephtelolahy Jul 19, 2024
93f7604
wip
stephtelolahy Jul 19, 2024
4975944
Clean AppState
stephtelolahy Jul 19, 2024
0d94316
wip
stephtelolahy Jul 19, 2024
6bdebd7
Update structure
stephtelolahy Jul 19, 2024
789ffd5
Move files
stephtelolahy Jul 20, 2024
c91a2b1
AppState
stephtelolahy Jul 20, 2024
9adb554
Update readme
stephtelolahy Jul 20, 2024
8a5d974
Fix warnings
stephtelolahy Jul 20, 2024
8ae00db
Fix warning
stephtelolahy Jul 20, 2024
788f228
Compile UI
stephtelolahy Jul 20, 2024
e59a9e6
Tests working aagain
stephtelolahy Jul 20, 2024
98aaf77
immutable inventory
stephtelolahy Jul 20, 2024
cda50b8
Add Structure
stephtelolahy Jul 21, 2024
b3f08d6
Rename Reducer
stephtelolahy Jul 21, 2024
9d060cf
Cleanup
stephtelolahy Jul 21, 2024
1da4794
Refactor
stephtelolahy Jul 21, 2024
a5926ab
Cleanup
stephtelolahy Jul 21, 2024
09c7b75
embed ui action
stephtelolahy Jul 21, 2024
af10dfd
Splash embed action
stephtelolahy Jul 21, 2024
60d6a09
Embed actions
stephtelolahy Jul 21, 2024
bc0165f
Embed settings action
stephtelolahy Jul 22, 2024
0cfc82b
wip
stephtelolahy Jul 22, 2024
b797c66
UI Presenter
stephtelolahy Jul 23, 2024
7164ac1
Fix tests
stephtelolahy Jul 23, 2024
e05bd00
Specify couple State-Action on Store
stephtelolahy Jul 23, 2024
f6fa625
App reducer
stephtelolahy Jul 23, 2024
02424b1
All at once
stephtelolahy Jul 23, 2024
675dea9
Add todo
stephtelolahy Jul 23, 2024
5c81340
Fix tests
stephtelolahy Jul 23, 2024
9e77fd6
Fix tests
stephtelolahy Jul 23, 2024
5a848d9
Cleanup
stephtelolahy Jul 23, 2024
79fb1fa
try to observe store event
stephtelolahy Jul 23, 2024
a9876cc
Fix tests
stephtelolahy Jul 23, 2024
6aaaa5e
Cleanup
stephtelolahy Jul 24, 2024
bfab9f3
NavigationCore
stephtelolahy Jul 24, 2024
e49674a
Rename modules
stephtelolahy Jul 24, 2024
634b398
Rename modules
stephtelolahy Jul 24, 2024
bc0e866
Cleanup
stephtelolahy Jul 24, 2024
d61ee07
Fix compilation
stephtelolahy Jul 24, 2024
86b5b4f
Completed
stephtelolahy Jul 24, 2024
2b8a786
Cleanup
stephtelolahy Jul 25, 2024
05344df
Rename Utilities
stephtelolahy Jul 26, 2024
e3e04e1
Rename utilities
stephtelolahy Jul 26, 2024
f2ab3f2
Clean tests
stephtelolahy Jul 26, 2024
628f35b
Fix warning
stephtelolahy Jul 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
48 changes: 20 additions & 28 deletions App/WildWestOnline.xctestplan
Original file line number Diff line number Diff line change
Expand Up @@ -28,80 +28,72 @@
"parallelizable" : true,
"target" : {
"containerPath" : "container:WildWestOnline",
"identifier" : "AppTests",
"name" : "AppTests"
"identifier" : "GameCoreTests",
"name" : "GameCoreTests"
}
},
{
"parallelizable" : true,
"target" : {
"containerPath" : "container:WildWestOnline",
"identifier" : "GamePlayTests",
"name" : "GamePlayTests"
"identifier" : "AppCoreTests",
"name" : "AppCoreTests"
}
},
{
"parallelizable" : true,
"target" : {
"containerPath" : "container:WildWestOnline",
"identifier" : "GameCoreTests",
"name" : "GameCoreTests"
"identifier" : "NavigationCoreTests",
"name" : "NavigationCoreTests"
}
},
{
"parallelizable" : true,
"target" : {
"containerPath" : "container:WildWestOnline",
"identifier" : "AppCoreTests",
"name" : "AppCoreTests"
"identifier" : "CardsDataTests",
"name" : "CardsDataTests"
}
},
{
"parallelizable" : true,
"target" : {
"containerPath" : "container:WildWestOnline",
"identifier" : "CardsRepositoryTests",
"name" : "CardsRepositoryTests"
"identifier" : "SerializationTests",
"name" : "SerializationTests"
}
},
{
"parallelizable" : true,
"target" : {
"containerPath" : "container:WildWestOnline",
"identifier" : "HomeTests",
"name" : "HomeTests"
"identifier" : "GameUITests",
"name" : "GameUITests"
}
},
{
"parallelizable" : true,
"target" : {
"containerPath" : "container:WildWestOnline",
"identifier" : "SettingsCoreTests",
"name" : "SettingsCoreTests"
"identifier" : "HomeUITests",
"name" : "HomeUITests"
}
},
{
"parallelizable" : true,
"target" : {
"containerPath" : "container:WildWestOnline",
"identifier" : "SettingsTests",
"name" : "SettingsTests"
"identifier" : "SettingsUITests",
"name" : "SettingsUITests"
}
},
{
"parallelizable" : true,
"target" : {
"containerPath" : "container:WildWestOnline",
"identifier" : "SplashTests",
"name" : "SplashTests"
"identifier" : "SplashUITests",
"name" : "SplashUITests"
}
},
{
"parallelizable" : true,
"target" : {
"containerPath" : "container:WildWestOnline",
"identifier" : "UtilsTests",
"name" : "UtilsTests"
"identifier" : "SettingsCoreTests",
"name" : "SettingsCoreTests"
}
}
],
Expand Down
93 changes: 83 additions & 10 deletions App/WildWestOnlineApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
//
// Created by Hugues Telolahy on 02/04/2023.
//
import App
import AppCore
import CardsRepository
import CardsData
import GameCore
import GameUI
import HomeUI
import Redux
import SettingsUI
import SettingsCore
import SettingsRepository
import SettingsData
import SplashUI
import SwiftUI
import Theme

Expand All @@ -20,7 +23,7 @@ struct WildWestOnlineApp: App {

var body: some Scene {
WindowGroup {
AppView {
ContentView {
createStore()
}
.environment(\.colorScheme, .light)
Expand All @@ -29,12 +32,71 @@ struct WildWestOnlineApp: App {
}
}

private func createStore() -> Store<AppState> {
struct ContentView: View {
@StateObject private var store: Store<AppState, Any>

public init(store: @escaping () -> Store<AppState, Any>) {
// SwiftUI ensures that the following initialization uses the
// closure only once during the lifetime of the view.
_store = StateObject(wrappedValue: store())
}

var body: some View {
Group {
switch store.state.screens.last {
case .splash:
SplashView {
store.projection(SplashView.deriveState, SplashView.embedAction)
}

case .home:
HomeView {
store.projection(HomeView.deriveState, HomeView.embedAction)
}

case .game:
GameView {
store.projection(GameView.deriveState, GameView.embedAction)
}

default:
EmptyView()
}
}
.sheet(isPresented: Binding<Bool>(
get: { store.state.screens.last == .settings },
set: { _ in }
), onDismiss: {
}, content: {
SettingsView {
store.projection(SettingsView.deriveState, SettingsView.embedAction)
}
})
.foregroundColor(.primary)
}
}

#Preview {
ContentView {
Store(initial: .preview)
}
}

private extension AppState {
static var preview: Self {
.init(
screens: [.home],
settings: SettingsState.makeBuilder().build(),
inventory: Inventory.makeBuilder().build()
)
}
}

private func createStore() -> Store<AppState, Any> {
let settingsService = SettingsRepository()
let cardsService = CardsRepository()

let settings = SettingsState.makeBuilder()
.withInventory(cardsService.inventory)
.withPlayersCount(settingsService.playersCount())
.withWaitDelayMilliseconds(settingsService.waitDelayMilliseconds())
.withSimulation(settingsService.isSimulationEnabled())
Expand All @@ -43,15 +105,26 @@ private func createStore() -> Store<AppState> {

let initialState = AppState(
screens: [.splash],
settings: settings
settings: settings,
inventory: cardsService.inventory
)

return Store<AppState>(
return Store<AppState, Any>(
initial: initialState,
reducer: AppState.reducer,
middlewares: [
Middlewares.lift(Middlewares.updateGame(), stateMap: { $0.game }),
Middlewares.lift(Middlewares.saveSettings(with: settingsService), stateMap: { $0.settings }),
Middlewares.lift(
Middlewares.updateGame(),
deriveState: { $0.game },
deriveAction: { $0 as? GameAction },
embedAction: { action, _ in action }
),
Middlewares.lift(
Middlewares.saveSettings(with: settingsService),
deriveState: { $0.settings },
deriveAction: { $0 as? SettingsAction },
embedAction: { action, _ in action }
),
Middlewares.logger()
]
)
Expand Down
36 changes: 0 additions & 36 deletions Docs/STRUCTURE.md

This file was deleted.

Binary file removed Docs/app_modules.png
Binary file not shown.
Binary file added Docs/project_structure.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,19 @@ Redux architecture is meant to protect changes in an application’s state. It f
The app should have a single real Store, holding a single source-of-truth.
However, we can "derive" this store to small subsets, called store projections, that will handle a smaller part of the state for each screen. So we can map back-and-forth to the original store types.

![](Docs/app_modules.png)
#### Clean+Redux Principles
- App = ∑ View(ViewState, ViewAction)
- ViewState = derive(AppState)
- ViewAction = extract(AppAction)
- AppState = ∑ State
- State = Reducer(State, Action)
- Action = Middleware(State, Action, Service)

#### Project structure

The project is composed of SwiftPackage products with the following structure.

![](Docs/project_structure.png)


### Sequence diagram
Expand Down
45 changes: 40 additions & 5 deletions WildWestOnline.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@
objects = {

/* Begin PBXBuildFile section */
153E2F5B2C50C4A1003E9F54 /* CardsData in Frameworks */ = {isa = PBXBuildFile; productRef = 153E2F5A2C50C4A1003E9F54 /* CardsData */; };
153E2F5D2C50C4A1003E9F54 /* SettingsData in Frameworks */ = {isa = PBXBuildFile; productRef = 153E2F5C2C50C4A1003E9F54 /* SettingsData */; };
153E2F5F2C50C6DE003E9F54 /* GameUI in Frameworks */ = {isa = PBXBuildFile; productRef = 153E2F5E2C50C6DE003E9F54 /* GameUI */; };
153E2F612C50C6DE003E9F54 /* HomeUI in Frameworks */ = {isa = PBXBuildFile; productRef = 153E2F602C50C6DE003E9F54 /* HomeUI */; };
153E2F632C50C6DE003E9F54 /* SettingsUI in Frameworks */ = {isa = PBXBuildFile; productRef = 153E2F622C50C6DE003E9F54 /* SettingsUI */; };
153E2F652C50C6DE003E9F54 /* SplashUI in Frameworks */ = {isa = PBXBuildFile; productRef = 153E2F642C50C6DE003E9F54 /* SplashUI */; };
2699EF0129D93AD00030ACCD /* WildWestOnlineApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2699EF0029D93AD00030ACCD /* WildWestOnlineApp.swift */; };
2699EF0529D93AD10030ACCD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2699EF0429D93AD10030ACCD /* Assets.xcassets */; };
26E6D66D2B2480C1009643EB /* App in Frameworks */ = {isa = PBXBuildFile; productRef = 26E6D66C2B2480C1009643EB /* App */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -27,7 +32,12 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
26E6D66D2B2480C1009643EB /* App in Frameworks */,
153E2F5F2C50C6DE003E9F54 /* GameUI in Frameworks */,
153E2F652C50C6DE003E9F54 /* SplashUI in Frameworks */,
153E2F612C50C6DE003E9F54 /* HomeUI in Frameworks */,
153E2F632C50C6DE003E9F54 /* SettingsUI in Frameworks */,
153E2F5D2C50C4A1003E9F54 /* SettingsData in Frameworks */,
153E2F5B2C50C4A1003E9F54 /* CardsData in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -88,7 +98,12 @@
);
name = WildWestOnline;
packageProductDependencies = (
26E6D66C2B2480C1009643EB /* App */,
153E2F5A2C50C4A1003E9F54 /* CardsData */,
153E2F5C2C50C4A1003E9F54 /* SettingsData */,
153E2F5E2C50C6DE003E9F54 /* GameUI */,
153E2F602C50C6DE003E9F54 /* HomeUI */,
153E2F622C50C6DE003E9F54 /* SettingsUI */,
153E2F642C50C6DE003E9F54 /* SplashUI */,
);
productName = ReduxGameDSL;
productReference = 2699EEFD29D93AD00030ACCD /* WildWestOnline.app */;
Expand Down Expand Up @@ -375,9 +390,29 @@
/* End XCConfigurationList section */

/* Begin XCSwiftPackageProductDependency section */
26E6D66C2B2480C1009643EB /* App */ = {
153E2F5A2C50C4A1003E9F54 /* CardsData */ = {
isa = XCSwiftPackageProductDependency;
productName = App;
productName = CardsData;
};
153E2F5C2C50C4A1003E9F54 /* SettingsData */ = {
isa = XCSwiftPackageProductDependency;
productName = SettingsData;
};
153E2F5E2C50C6DE003E9F54 /* GameUI */ = {
isa = XCSwiftPackageProductDependency;
productName = GameUI;
};
153E2F602C50C6DE003E9F54 /* HomeUI */ = {
isa = XCSwiftPackageProductDependency;
productName = HomeUI;
};
153E2F622C50C6DE003E9F54 /* SettingsUI */ = {
isa = XCSwiftPackageProductDependency;
productName = SettingsUI;
};
153E2F642C50C6DE003E9F54 /* SplashUI */ = {
isa = XCSwiftPackageProductDependency;
productName = SplashUI;
};
/* End XCSwiftPackageProductDependency section */
};
Expand Down
Loading
Loading