mirror of
https://github.com/pointfreeco/swift-composable-architecture.git
synced 2025-12-24 12:14:25 +01:00
* Re-introduce legacy alert and action sheet APIs With 1.0 we removed `View.alert(_ store:dismiss:)`, which was a problematic API. It used different SwiftUI alert APIs depending on OS version, which led to different runtime behavior for apps using it depending on the OS and an unfixable bug: https://github.com/pointfreeco/swift-composable-architecture/issues/1981 This was reason enough to remove the API, but it was also an API that predated the Composable Architecture's navigation tools. This meant it awkwardly took an explicit `dismiss:` action, and it was up to the developer to remember to use this action to manually clear out state. The Composable Architecture's navigation tools handle dismissal automatically once integrated, but also only support iOS 15 alerts at the moment. This PR addresses the above: it introduces view modifiers for the old iOS 13 style of alert (and `actionSheet`) providing some presentation domain, and it will automatically dismiss these modals accordingly: - It adds `View.legacyAlert(store:)`. While we don't love `legacyAlert`, we haven't come up with a better option. We need a statically unique view modifier, and `alert(store:)` is already in use for iOS 15. We're open to suggestions here, though! - It adds `View.actionSheet(store:)`. * wip
89 lines
2.9 KiB
Swift
89 lines
2.9 KiB
Swift
import SwiftUI
|
|
|
|
extension View {
|
|
/// Displays an action sheet when then store's state becomes non-`nil`, and dismisses it when it
|
|
/// becomes `nil`.
|
|
///
|
|
/// - Parameters:
|
|
/// - store: A store that is focused on ``PresentationState`` and ``PresentationAction`` for an
|
|
/// alert.
|
|
/// - toDestinationState: A transformation to extract alert state from the presentation state.
|
|
/// - fromDestinationAction: A transformation to embed alert actions into the presentation
|
|
/// action.
|
|
@available(
|
|
iOS,
|
|
introduced: 13,
|
|
deprecated: 100000,
|
|
message: "use 'View.confirmationDialog(store:)' instead."
|
|
)
|
|
@available(macOS, unavailable)
|
|
@available(
|
|
tvOS,
|
|
introduced: 13,
|
|
deprecated: 100000,
|
|
message: "use 'View.confirmationDialog(store:)' instead."
|
|
)
|
|
@available(
|
|
watchOS,
|
|
introduced: 6,
|
|
deprecated: 100000,
|
|
message: "use 'View.confirmationDialog(store:)' instead."
|
|
)
|
|
public func actionSheet<ButtonAction>(
|
|
store: Store<
|
|
PresentationState<ConfirmationDialogState<ButtonAction>>, PresentationAction<ButtonAction>
|
|
>
|
|
) -> some View {
|
|
self.actionSheet(store: store, state: { $0 }, action: { $0 })
|
|
}
|
|
|
|
/// Displays an alert when then store's state becomes non-`nil`, and dismisses it when it becomes
|
|
/// `nil`.
|
|
///
|
|
/// - Parameters:
|
|
/// - store: A store that is focused on ``PresentationState`` and ``PresentationAction`` for an
|
|
/// alert.
|
|
/// - toDestinationState: A transformation to extract alert state from the presentation state.
|
|
/// - fromDestinationAction: A transformation to embed alert actions into the presentation
|
|
/// action.
|
|
@available(
|
|
iOS,
|
|
introduced: 13,
|
|
deprecated: 100000,
|
|
message: "use 'View.confirmationDialog(store:state:action:)' instead."
|
|
)
|
|
@available(macOS, unavailable)
|
|
@available(
|
|
tvOS,
|
|
introduced: 13,
|
|
deprecated: 100000,
|
|
message: "use 'View.confirmationDialog(store:state:action:)' instead."
|
|
)
|
|
@available(
|
|
watchOS,
|
|
introduced: 6,
|
|
deprecated: 100000,
|
|
message: "use 'View.confirmationDialog(store:state:action:)' instead."
|
|
)
|
|
public func actionSheet<State, Action, ButtonAction>(
|
|
store: Store<PresentationState<State>, PresentationAction<Action>>,
|
|
state toDestinationState: @escaping (_ state: State) -> ConfirmationDialogState<ButtonAction>?,
|
|
action fromDestinationAction: @escaping (_ alertAction: ButtonAction) -> Action
|
|
) -> some View {
|
|
self.presentation(
|
|
store: store, state: toDestinationState, action: fromDestinationAction
|
|
) { `self`, $item, _ in
|
|
let actionSheetState = store.state.value.wrappedValue.flatMap(toDestinationState)
|
|
self.actionSheet(item: $item) { _ in
|
|
ActionSheet(actionSheetState!) { action in
|
|
if let action = action {
|
|
store.send(.presented(fromDestinationAction(action)))
|
|
} else {
|
|
store.send(.dismiss)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|