Skip to content

Commit 6133d3e

Browse files
committed
Fix bindable deprecations (#815)
* Fix Bindable Deprecations * More CI * wip * wip * wip * wip
1 parent 343bf2a commit 6133d3e

File tree

10 files changed

+277
-234
lines changed

10 files changed

+277
-234
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ jobs:
1414
strategy:
1515
matrix:
1616
xcode:
17+
- 12.4
1718
- 12.5
1819
- '13.0'
1920
steps:
@@ -33,6 +34,7 @@ jobs:
3334
strategy:
3435
matrix:
3536
xcode:
37+
- 12.4
3638
- 12.5
3739
- '13.0'
3840
steps:

Examples/CaseStudies/SwiftUICaseStudies/00-Core.swift

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@ struct RootState {
77
var alertAndActionSheet = AlertAndSheetState()
88
var animation = AnimationsState()
99
var bindingBasics = BindingBasicsState()
10-
var bindingForm = BindingFormState()
10+
#if compiler(>=5.4)
11+
var bindingForm = BindingFormState()
12+
#endif
1113
var clock = ClockState()
1214
var counter = CounterState()
1315
var dieRoll = DieRollState()
1416
var effectsBasics = EffectsBasicsState()
1517
var effectsCancellation = EffectsCancellationState()
1618
var effectsTimers = TimersState()
1719
var episodes = EpisodesState(episodes: .mocks)
18-
var focusDemo = FocusDemoState()
20+
#if compiler(>=5.5)
21+
var focusDemo = FocusDemoState()
22+
#endif
1923
var lifecycle = LifecycleDemoState()
2024
var loadThenNavigate = LoadThenNavigateState()
2125
var loadThenNavigateList = LoadThenNavigateListState()
@@ -39,14 +43,18 @@ enum RootAction {
3943
case alertAndActionSheet(AlertAndSheetAction)
4044
case animation(AnimationsAction)
4145
case bindingBasics(BindingBasicsAction)
42-
case bindingForm(BindingFormAction)
46+
#if compiler(>=5.4)
47+
case bindingForm(BindingFormAction)
48+
#endif
4349
case clock(ClockAction)
4450
case counter(CounterAction)
4551
case dieRoll(DieRollAction)
4652
case effectsBasics(EffectsBasicsAction)
4753
case effectsCancellation(EffectsCancellationAction)
4854
case episodes(EpisodesAction)
49-
case focusDemo(FocusDemoAction)
55+
#if compiler(>=5.5)
56+
case focusDemo(FocusDemoAction)
57+
#endif
5058
case lifecycle(LifecycleDemoAction)
5159
case loadThenNavigate(LoadThenNavigateAction)
5260
case loadThenNavigateList(LoadThenNavigateListAction)
@@ -120,12 +128,19 @@ let rootReducer = Reducer<RootState, RootAction, RootEnvironment>.combine(
120128
action: /RootAction.bindingBasics,
121129
environment: { _ in .init() }
122130
),
123-
bindingFormReducer
124-
.pullback(
125-
state: \.bindingForm,
126-
action: /RootAction.bindingForm,
127-
environment: { _ in .init() }
128-
),
131+
.init { state, action, environment in
132+
#if compiler(>=5.4)
133+
return bindingFormReducer
134+
.pullback(
135+
state: \.bindingForm,
136+
action: /RootAction.bindingForm,
137+
environment: { _ in .init() }
138+
)
139+
.run(&state, action, environment)
140+
#else
141+
return .none
142+
#endif
143+
},
129144
clockReducer
130145
.pullback(
131146
state: \.clock,
@@ -162,12 +177,19 @@ let rootReducer = Reducer<RootState, RootAction, RootEnvironment>.combine(
162177
action: /RootAction.episodes,
163178
environment: { .init(favorite: $0.favorite, mainQueue: $0.mainQueue) }
164179
),
165-
focusDemoReducer
166-
.pullback(
167-
state: \.focusDemo,
168-
action: /RootAction.focusDemo,
169-
environment: { _ in .init() }
170-
),
180+
.init { state, action, environment in
181+
#if compiler(>=5.5)
182+
return focusDemoReducer
183+
.pullback(
184+
state: \.focusDemo,
185+
action: /RootAction.focusDemo,
186+
environment: { _ in .init() }
187+
)
188+
.run(&state, action, environment)
189+
#else
190+
return .none
191+
#endif
192+
},
171193
lifecycleDemoReducer
172194
.pullback(
173195
state: \.lifecycle,

Examples/CaseStudies/SwiftUICaseStudies/00-RootView.swift

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,17 @@ struct RootView: View {
4141
)
4242
)
4343

44-
NavigationLink(
45-
"Form bindings",
46-
destination: BindingFormView(
47-
store: self.store.scope(
48-
state: \.bindingForm,
49-
action: RootAction.bindingForm
44+
#if compiler(>=5.4)
45+
NavigationLink(
46+
"Form bindings",
47+
destination: BindingFormView(
48+
store: self.store.scope(
49+
state: \.bindingForm,
50+
action: RootAction.bindingForm
51+
)
5052
)
5153
)
52-
)
54+
#endif
5355

5456
NavigationLink(
5557
"Optional state",
Lines changed: 100 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,113 @@
1-
import ComposableArchitecture
2-
import SwiftUI
3-
4-
private let readMe = """
5-
This file demonstrates how to handle two-way bindings in the Composable Architecture using \
6-
bindable state and actions.
7-
8-
Bindable state and actions allow you to safely eliminate the boilerplate caused by needing to \
9-
have a unique action for every UI control. Instead, all UI bindings can be consolidated into a \
10-
single `binding` action that holds onto a `BindingAction` value, and all bindable state can be \
11-
safeguarded with the `BindableState` property wrapper.
12-
13-
It is instructive to compare this case study to the "Binding Basics" case study.
14-
"""
15-
16-
// The state for this screen holds a bunch of values that will drive
17-
struct BindingFormState: Equatable {
18-
@BindableState var sliderValue = 5.0
19-
@BindableState var stepCount = 10
20-
@BindableState var text = ""
21-
@BindableState var toggleIsOn = false
22-
}
23-
24-
enum BindingFormAction: BindableAction, Equatable {
25-
case binding(BindingAction<BindingFormState>)
26-
case resetButtonTapped
27-
}
28-
29-
struct BindingFormEnvironment {}
30-
31-
let bindingFormReducer = Reducer<
32-
BindingFormState, BindingFormAction, BindingFormEnvironment
33-
> {
34-
state, action, _ in
35-
switch action {
36-
case .binding(\.$stepCount):
37-
state.sliderValue = .minimum(state.sliderValue, Double(state.stepCount))
38-
return .none
39-
40-
case .binding:
41-
return .none
42-
43-
case .resetButtonTapped:
44-
state = .init()
45-
return .none
1+
#if compiler(>=5.4)
2+
import ComposableArchitecture
3+
import SwiftUI
4+
5+
private let readMe = """
6+
This file demonstrates how to handle two-way bindings in the Composable Architecture using \
7+
bindable state and actions.
8+
9+
Bindable state and actions allow you to safely eliminate the boilerplate caused by needing to \
10+
have a unique action for every UI control. Instead, all UI bindings can be consolidated into a \
11+
single `binding` action that holds onto a `BindingAction` value, and all bindable state can be \
12+
safeguarded with the `BindableState` property wrapper.
13+
14+
It is instructive to compare this case study to the "Binding Basics" case study.
15+
"""
16+
17+
// The state for this screen holds a bunch of values that will drive
18+
struct BindingFormState: Equatable {
19+
@BindableState var sliderValue = 5.0
20+
@BindableState var stepCount = 10
21+
@BindableState var text = ""
22+
@BindableState var toggleIsOn = false
4623
}
47-
}
48-
.binding()
49-
50-
struct BindingFormView: View {
51-
let store: Store<BindingFormState, BindingFormAction>
52-
53-
var body: some View {
54-
WithViewStore(self.store) { viewStore in
55-
Form {
56-
Section(header: Text(template: readMe, .caption)) {
57-
HStack {
58-
TextField("Type here", text: viewStore.binding(\.$text))
59-
.disableAutocorrection(true)
60-
.foregroundColor(viewStore.toggleIsOn ? .gray : .primary)
61-
62-
Text(alternate(viewStore.text))
63-
}
64-
.disabled(viewStore.toggleIsOn)
6524

66-
Toggle("Disable other controls", isOn: viewStore.binding(\.$toggleIsOn))
25+
enum BindingFormAction: BindableAction, Equatable {
26+
case binding(BindingAction<BindingFormState>)
27+
case resetButtonTapped
28+
}
6729

68-
Stepper(value: viewStore.binding(\.$stepCount), in: 0...100) {
69-
Text("Max slider value: \(viewStore.stepCount)")
70-
.font(Font.body.monospacedDigit())
71-
}
72-
.disabled(viewStore.toggleIsOn)
30+
struct BindingFormEnvironment {}
7331

74-
HStack {
75-
Text("Slider value: \(Int(viewStore.sliderValue))")
76-
.font(Font.body.monospacedDigit())
32+
let bindingFormReducer = Reducer<
33+
BindingFormState, BindingFormAction, BindingFormEnvironment
34+
> {
35+
state, action, _ in
36+
switch action {
37+
case .binding(\.$stepCount):
38+
state.sliderValue = .minimum(state.sliderValue, Double(state.stepCount))
39+
return .none
7740

78-
Slider(value: viewStore.binding(\.$sliderValue), in: 0...Double(viewStore.stepCount))
41+
case .binding:
42+
return .none
43+
44+
case .resetButtonTapped:
45+
state = .init()
46+
return .none
47+
}
48+
}
49+
.binding()
50+
51+
struct BindingFormView: View {
52+
let store: Store<BindingFormState, BindingFormAction>
53+
54+
var body: some View {
55+
WithViewStore(self.store) { viewStore in
56+
Form {
57+
Section(header: Text(template: readMe, .caption)) {
58+
HStack {
59+
TextField("Type here", text: viewStore.binding(\.$text))
60+
.disableAutocorrection(true)
61+
.foregroundColor(viewStore.toggleIsOn ? .gray : .primary)
62+
63+
Text(alternate(viewStore.text))
64+
}
65+
.disabled(viewStore.toggleIsOn)
66+
67+
Toggle("Disable other controls", isOn: viewStore.binding(\.$toggleIsOn))
68+
69+
Stepper(value: viewStore.binding(\.$stepCount), in: 0...100) {
70+
Text("Max slider value: \(viewStore.stepCount)")
71+
.font(Font.body.monospacedDigit())
72+
}
73+
.disabled(viewStore.toggleIsOn)
74+
75+
HStack {
76+
Text("Slider value: \(Int(viewStore.sliderValue))")
77+
.font(Font.body.monospacedDigit())
78+
79+
Slider(value: viewStore.binding(\.$sliderValue), in: 0...Double(viewStore.stepCount))
80+
}
81+
.disabled(viewStore.toggleIsOn)
7982
}
80-
.disabled(viewStore.toggleIsOn)
8183
}
8284
}
85+
.navigationBarTitle("Bindings form")
8386
}
84-
.navigationBarTitle("Bindings form")
8587
}
86-
}
87-
88-
private func alternate(_ string: String) -> String {
89-
string
90-
.enumerated()
91-
.map { idx, char in
92-
idx.isMultiple(of: 2)
93-
? char.uppercased()
94-
: char.lowercased()
95-
}
96-
.joined()
97-
}
98-
99-
struct BindingFormView_Previews: PreviewProvider {
100-
static var previews: some View {
101-
NavigationView {
102-
BindingFormView(
103-
store: Store(
104-
initialState: BindingFormState(),
105-
reducer: bindingFormReducer,
106-
environment: BindingFormEnvironment()
88+
89+
private func alternate(_ string: String) -> String {
90+
string
91+
.enumerated()
92+
.map { idx, char in
93+
idx.isMultiple(of: 2)
94+
? char.uppercased()
95+
: char.lowercased()
96+
}
97+
.joined()
98+
}
99+
100+
struct BindingFormView_Previews: PreviewProvider {
101+
static var previews: some View {
102+
NavigationView {
103+
BindingFormView(
104+
store: Store(
105+
initialState: BindingFormState(),
106+
reducer: bindingFormReducer,
107+
environment: BindingFormEnvironment()
108+
)
107109
)
108-
)
110+
}
109111
}
110112
}
111-
}
113+
#endif

0 commit comments

Comments
 (0)