aarsland
(Andreas Aarsland)
June 11, 2020, 7:24pm
1
I am trying to show a modal sheet. I can do this with a @State
property, but I need to set the state for this boolean in my reducer. Have anyone done something like this with success?
.sheet(isPresented: viewStore.binding(
get: { $0.showEditIntro },
send: ProfileAction.editIntro
)) {
Text("Show this text when showEditIntro == true")
}
I use a button to toggle the value for the variable, and I can see that it gets updated, but the sheet does not appear. Could not find any example on presenting sheet in TCA repo.
moebius
(Steiner Hannes)
June 11, 2020, 8:01pm
2
Could you provide some more code?
I have a working example, but there is no real difference between it and the binding on navigation:
.sheet(isPresented: self.viewStore.binding(
get: { $0.isDateViewPresented },
send: NewEntryAction.setDateView(isPresented:))
) { ... }
Action:
enum NewEntryAction: Equatable {
case setDateView(isPresented: Bool)
...
}
In the reducer:
case let .setDateView(isPresented: isPresented):
state.isDateViewPresented = isPresented
return .none
The state:
public struct NewEntryState: Equatable {
var isDateViewPresented: Bool = false
...
}
Hope that helps
1 Like
mbrandonw
(Brandon Williams)
June 11, 2020, 8:02pm
3
Yeah this is definitely possible, and we have two case studies that do just this:
And as @moebius says, if you share some of your action/reducer code it might be easier to see what is going wrong
1 Like
aarsland
(Andreas Aarsland)
June 11, 2020, 8:30pm
4
Thank you very much! It was all my fault - had a sloppy implementation of:
static func == (lhs: ProfileState, rhs: ProfileState) -> Bool
When learning something new, it always helps to know that it should be possible! TCA looks great - keep up the good work!
1 Like
aarsland
(Andreas Aarsland)
August 11, 2020, 1:21pm
5
Is there any example of doing this with the .sheet(item: <Binding>)
variant? You explaind this generally with SwiftUI in the episode: Episode #109: Composable SwiftUI Bindings: The Point
We don't define a helper sheet
method in TCA right now, but you have a couple options:
Avoid the extra state but still use sheet(isPresented:)
by deriving the view store binding in terms of nil
state:
.sheet(
isPresented: viewStore.binding(
get: { $0.optionalCounter != nil },
send: LoadThenPresentAction.setSheet(isPresented:)
)
) {
IfLetStore(...)
}
Use sheet(item:)
with a view store binding but ignore the argument passed to the sheet's destination block:
enum LoadThenPresentAction {
...
case setSheet(item: CounterState?)
}
.sheet(
item: viewStore.binding(
get: \.optionalCounter,
send: LoadThenPresentAction.setSheet(item:)
)
) { _ in
IfLetStore(...)
}
Many times it is useful to separate presentation state from the optionality of the state itself, though, for example if you have some global app state that can be presented and you don't want that state's optionality to drive presentation.
1 Like
.sheet(
isPresented: viewStore.binding(
get: { $0.isSheetPresented },
send: Campaigns.ViewAction.setSheet(isPresented:)
)
) {
if viewStore.isNewCampaign {
IfLetStore(
self.store.scope(
state: { $0.selection }, action: CampaignsAction.campaign),
then: NewCampaign.init(store:),
else: ActivityIndicator()
)
} else {
Text("Show Campaign")
}
}
[...]
case .setSelection(isPresented: true):
guard state.selection != nil else { return .init(value: .newCampaign) }
state.isSelectionPresented = true
return .none
case .setSelection(isPresented: false):
state.isNewCampaign = false
state.isSelectionPresented = false
return .none
I don't understand how to the prevent the binding from sending a second .setSelection(isPresented: false) after I send .setSelection(isPresented: false)
myself.
1 Like