Skip to content

Commit 9d8ba9b

Browse files
authored
Merge pull request #38 from CombineCommunity/feature/rewrite-statemachine-dsl
Feature/rewrite statemachine dsl
2 parents ceb6c2b + 31bbd72 commit 9d8ba9b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1604
-796
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
**v0.3.0 - Tyranus**:
22

33
- Feedback: introduce the "on:" keyword to explicitly declare the type of state that concerns the side effect
4+
- Feedback: replace the parameter "sideEffect" by "perform" to have a nice readable sentence: ...(on: Loading.self, ..., perform: sideEffect)
5+
- State Machine: introduce a new DSL based on From/On that allows to group transitions from the same state type
6+
- State Machine: provide assert functions to ease the unit tests of transitions
47

58
**v0.2.0 - Vader**:
69

Examples/Examples.xcodeproj/project.pbxproj

+195-22
Large diffs are not rendered by default.

Examples/Examples.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
"pins": [
44
{
55
"package": "Feedbacks",
6-
"repositoryURL": "[email protected]:twittemb/Feedbacks.git",
6+
"repositoryURL": "[email protected]:CombineCommunity/Feedbacks.git",
77
"state": {
88
"branch": null,
9-
"revision": "ceca7e90a065cbb67346d0234243f598500ddfc5",
9+
"revision": "826c7798699acd5a9d0348049eaeb8917399a94f",
1010
"version": null
1111
}
1212
}

Examples/Examples/ContentView.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ struct ContentView: View {
2424
destination: GifList.RootView(
2525
system: GifList
2626
.System
27-
.gifOverview
27+
.gifs
2828
.uiSystem(viewStateFactory: GifList.ViewState.stateToViewState(state:))
2929
.run()
3030
))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// Counter.swift
3+
// Examples
4+
//
5+
// Created by Thibault Wittemberg on 2021-02-28.
6+
//
7+
8+
struct Counter: Equatable {
9+
let value: Int
10+
let min: Int
11+
let max: Int
12+
13+
func decrease() -> Counter {
14+
Counter(value: self.value - 1, min: self.min, max: self.max)
15+
}
16+
17+
func increase() -> Counter {
18+
Counter(value: self.value + 1, min: self.min, max: self.max)
19+
}
20+
}

Examples/Examples/CounterApp/System/CounterApp+Events.swift

+1-6
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,8 @@ extension CounterApp {
1414

1515
extension CounterApp.Events {
1616
struct TogglePause: Event {}
17-
18-
struct Reset: Event {
19-
let value: Int
20-
}
21-
17+
struct Reset: Event {}
2218
struct Increase: Event {}
23-
2419
struct Decrease: Event {}
2520
}
2621

Examples/Examples/CounterApp/System/CounterApp+SideEffects.swift

+2-18
Original file line numberDiff line numberDiff line change
@@ -16,33 +16,17 @@ extension CounterApp {
1616

1717
extension CounterApp.SideEffects {
1818
// This effect will make the state decrease when it is already decreasing and not paused
19-
// When the state is equal to 0, then the effect asks for an increase
2019
static func decreaseEffect(state: CounterApp.States.Decreasing) -> AnyPublisher<Event, Never> {
2120
guard !state.isPaused else { return Empty().eraseToAnyPublisher() }
22-
23-
if state.value > 0 {
24-
return Just<Event>(CounterApp.Events.Decrease())
25-
.delay(for: 1, scheduler: DispatchQueue(label: UUID().uuidString))
26-
.eraseToAnyPublisher()
27-
}
28-
29-
return Just<Event>(CounterApp.Events.Increase())
21+
return Just<Event>(CounterApp.Events.Decrease())
3022
.delay(for: 1, scheduler: DispatchQueue(label: UUID().uuidString))
3123
.eraseToAnyPublisher()
3224
}
3325

3426
// This effect will make the state increase when it is already increasing and not paused
35-
// When the state is equal to 10, then the effect asks for a decrease
3627
static func increaseEffect(state: CounterApp.States.Increasing) -> AnyPublisher<Event, Never> {
3728
guard !state.isPaused else { return Empty().eraseToAnyPublisher() }
38-
39-
if state.value < 10 {
40-
return Just<Event>(CounterApp.Events.Increase())
41-
.delay(for: 1, scheduler: DispatchQueue(label: UUID().uuidString))
42-
.eraseToAnyPublisher()
43-
}
44-
45-
return Just<Event>(CounterApp.Events.Decrease())
29+
return Just<Event>(CounterApp.Events.Increase())
4630
.delay(for: 1, scheduler: DispatchQueue(label: UUID().uuidString))
4731
.eraseToAnyPublisher()
4832
}

Examples/Examples/CounterApp/System/CounterApp+States.swift

+6-6
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@ extension CounterApp {
1313
}
1414

1515
extension CounterApp.States {
16-
struct Fixed: State {
17-
let value: Int
16+
struct Fixed: State, Equatable {
17+
let counter: Counter
1818
}
1919

20-
struct Increasing: State {
21-
let value: Int
20+
struct Increasing: State, Equatable {
21+
let counter: Counter
2222
let isPaused: Bool
2323
}
2424

25-
struct Decreasing: State {
26-
let value: Int
25+
struct Decreasing: State, Equatable {
26+
let counter: Counter
2727
let isPaused: Bool
2828
}
2929
}

Examples/Examples/CounterApp/System/CounterApp+System.swift

+34-7
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,17 @@ extension CounterApp {
1515
extension CounterApp.System {
1616
static let counter = System {
1717
InitialState {
18-
CounterApp.States.Fixed(value: 10)
18+
CounterApp.States.Fixed(counter: Counter(value: 10, min: 0, max: 10))
1919
}
2020

2121
Feedbacks {
2222
Feedback(on: CounterApp.States.Decreasing.self,
2323
strategy: .cancelOnNewState,
24-
sideEffect: CounterApp.SideEffects.decreaseEffect(state:))
24+
perform: CounterApp.SideEffects.decreaseEffect(state:))
2525

2626
Feedback(on: CounterApp.States.Increasing.self,
2727
strategy: .cancelOnNewState,
28-
sideEffect: CounterApp.SideEffects.increaseEffect(state:))
28+
perform: CounterApp.SideEffects.increaseEffect(state:))
2929
}
3030
.onStateReceived {
3131
print("Counter: New state has been received: \($0)")
@@ -35,10 +35,37 @@ extension CounterApp.System {
3535
}
3636

3737
Transitions {
38-
CounterApp.Transitions.fixedTransition
39-
CounterApp.Transitions.resetTransition
40-
CounterApp.Transitions.decreasingTransitions
41-
CounterApp.Transitions.increasingTransitions
38+
From(CounterApp.States.Fixed.self) { state in
39+
On(CounterApp.Events.TogglePause.self, transitionTo: CounterApp.States.Decreasing(counter: state.counter, isPaused: false))
40+
}
41+
42+
From(AnyState.self) {
43+
On(CounterApp.Events.Reset.self) {
44+
CounterApp.States.Fixed(counter: Counter(value: 10, min: 0, max: 10))
45+
}
46+
}
47+
48+
From(CounterApp.States.Decreasing.self) { state in
49+
On(CounterApp.Events.TogglePause.self, transitionTo: CounterApp.States.Decreasing(counter: state.counter, isPaused: !state.isPaused))
50+
On(CounterApp.Events.Decrease.self) {
51+
guard !state.isPaused else { return state }
52+
if state.counter.value == state.counter.min {
53+
return CounterApp.States.Increasing(counter: state.counter.increase(), isPaused: false)
54+
}
55+
return CounterApp.States.Decreasing(counter: state.counter.decrease(), isPaused: false)
56+
}
57+
}
58+
59+
From(CounterApp.States.Increasing.self) { state in
60+
On(CounterApp.Events.TogglePause.self, transitionTo: CounterApp.States.Increasing(counter: state.counter, isPaused: !state.isPaused))
61+
On(CounterApp.Events.Increase.self) {
62+
guard !state.isPaused else { return state }
63+
if state.counter.value == state.counter.max {
64+
return CounterApp.States.Decreasing(counter: state.counter.decrease(), isPaused: false)
65+
}
66+
return CounterApp.States.Increasing(counter: state.counter.increase(), isPaused: false)
67+
}
68+
}
4269
}
4370
}
4471
}

Examples/Examples/CounterApp/System/CounterApp+Transitions.swift

-56
This file was deleted.

Examples/Examples/CounterApp/Views/CounterApp+RootView.swift

+7-7
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ extension CounterApp {
2929
HStack {
3030
Spacer()
3131
Button(action: {
32-
self.system.emit(CounterApp.Events.Reset(value: 10))
32+
self.system.emit(CounterApp.Events.Reset())
3333
}) {
3434
Text("Reset")
3535
.font(.system(size: 25))
@@ -66,9 +66,9 @@ extension CounterApp {
6666

6767
private func counterValue(from rawState: RawState) -> Int {
6868
switch rawState.state {
69-
case let fixed as CounterApp.States.Fixed: return fixed.value
70-
case let decreasing as CounterApp.States.Decreasing: return decreasing.value
71-
case let increasing as CounterApp.States.Increasing: return increasing.value
69+
case let fixed as CounterApp.States.Fixed: return fixed.counter.value
70+
case let decreasing as CounterApp.States.Decreasing: return decreasing.counter.value
71+
case let increasing as CounterApp.States.Increasing: return increasing.counter.value
7272
default: return 0
7373
}
7474
}
@@ -99,11 +99,11 @@ extension CounterApp {
9999
private func counterDescription(from rawState: RawState) -> String {
100100
switch rawState.state {
101101
case let fixed as CounterApp.States.Fixed:
102-
return "Fixed(value: \(fixed.value))"
102+
return "Fixed(value: \(fixed.counter.value))"
103103
case let decreasing as CounterApp.States.Decreasing:
104-
return "Decreasing(value: \(decreasing.value), paused: \(decreasing.isPaused))"
104+
return "Decreasing(value: \(decreasing.counter.value), paused: \(decreasing.isPaused))"
105105
case let increasing as CounterApp.States.Increasing:
106-
return "Increasing(value: \(increasing.value), paused: \(increasing.isPaused))"
106+
return "Increasing(value: \(increasing.counter.value), paused: \(increasing.isPaused))"
107107
default: return "undefined"
108108
}
109109
}

Examples/Examples/GiphyApp/Features/GifDetail/System/GifDetail+States.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@ extension GifDetail {
1212
}
1313

1414
extension GifDetail.States {
15-
struct Loading: State {}
15+
struct Loading: State, Equatable {}
1616

17-
struct Loaded: State {
17+
struct Loaded: State, Equatable {
1818
let gif: Gif
1919
let isFavorite: Bool
2020
}
2121

22-
struct TogglingFavorite: State {
22+
struct TogglingFavorite: State, Equatable {
2323
let gif: Gif
2424
let isFavorite: Bool
2525
}
2626

27-
struct Failed: State {}
27+
struct Failed: State, Equatable {}
2828
}

Examples/Examples/GiphyApp/Features/GifDetail/System/GifDetail+System.swift

+20-5
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ extension GifDetail.System {
3131
}
3232

3333
Feedbacks {
34-
Feedback(on: GifDetail.States.Loading.self, strategy: .cancelOnNewState, sideEffect: loadSideEffect)
34+
Feedback(on: GifDetail.States.Loading.self, strategy: .cancelOnNewState, perform: loadSideEffect)
3535
.execute(on: DispatchQueue(label: "Load Gif Queue"))
3636

37-
Feedback(on: GifDetail.States.TogglingFavorite.self, strategy: .cancelOnNewState, sideEffect: toggleFavoriteSideEffect)
37+
Feedback(on: GifDetail.States.TogglingFavorite.self, strategy: .cancelOnNewState, perform: toggleFavoriteSideEffect)
3838
.execute(on: DispatchQueue(label: "Toggle Favorite Queue"))
3939
}
4040
.onStateReceived {
@@ -45,9 +45,24 @@ extension GifDetail.System {
4545
}
4646

4747
Transitions {
48-
GifDetail.Transitions.loadingTransitions
49-
GifDetail.Transitions.loadedTransition
50-
GifDetail.Transitions.togglingTransitions
48+
From(GifDetail.States.Loading.self) {
49+
On(GifDetail.Events.LoadingIsComplete.self) { event in
50+
GifDetail.States.Loaded(gif: event.gif, isFavorite: event.isFavorite)
51+
}
52+
53+
On(GifDetail.Events.LoadingHasFailed.self, transitionTo: GifDetail.States.Failed())
54+
}
55+
56+
From(GifDetail.States.Loaded.self) { state in
57+
On(GifDetail.Events.ToggleFavorite.self, transitionTo: GifDetail.States.TogglingFavorite(gif: state.gif, isFavorite: !state.isFavorite))
58+
}
59+
60+
From(GifDetail.States.TogglingFavorite.self) {
61+
On(GifDetail.Events.LoadingIsComplete.self) { event in
62+
GifDetail.States.Loaded(gif: event.gif, isFavorite: event.isFavorite)
63+
}
64+
On(GifDetail.Events.LoadingHasFailed.self, transitionTo: GifDetail.States.Failed())
65+
}
5166
}
5267
}
5368
}

Examples/Examples/GiphyApp/Features/GifDetail/System/GifDetail+Transitions.swift

-35
This file was deleted.

0 commit comments

Comments
 (0)