Skip to content

Commit c5ba0e3

Browse files
andreyzstephencelis
authored andcommitted
UIKit helpers generating UIAlertController from AlertState & ActionSheetState (#563)
* UIKit helpers generating UIAlertController from AlertState & ActionSheetState * Exclude helpers from platforms not supporting UIKit (macOS) * Exclude watchOS * Update public API * Update AlertStateUIKit.swift Co-authored-by: Stephen Celis <[email protected]>
1 parent 3bf6162 commit c5ba0e3

File tree

1 file changed

+110
-0
lines changed

1 file changed

+110
-0
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#if canImport(UIKit) && !os(watchOS)
2+
import UIKit
3+
4+
@available(iOS 13, *)
5+
@available(macCatalyst 13, *)
6+
@available(macOS, unavailable)
7+
@available(tvOS 13, *)
8+
@available(watchOS, unavailable)
9+
extension UIAlertController {
10+
/// Creates a `UIAlertController` from `AlertState`.
11+
///
12+
/// ```swift
13+
/// class ParentViewController: UIViewController {
14+
/// let store: Store<ParentState, ParentAction>
15+
/// let viewStore: ViewStore<ViewState, ViewAction>
16+
/// private var cancellables: Set<AnyCancellable> = []
17+
/// private weak var alertController: UIAlertController?
18+
/// ...
19+
/// func viewDidLoad() {
20+
/// ...
21+
/// viewStore.publisher
22+
/// .settingsAlert
23+
/// .sink { [weak self] alert in
24+
/// guard let self = self else { return }
25+
/// if let alert = alert {
26+
/// let alertController = UIAlertController(state: alert, send: {
27+
/// self.viewStore.send(.settings($0))
28+
/// })
29+
/// self.present(alertController, animated: true, completion: nil)
30+
/// self.alertController = alertController
31+
/// } else {
32+
/// self.alertController?.dismiss(animated: true, completion: nil)
33+
/// self.alertController = nil
34+
/// }
35+
/// }
36+
/// .store(in: &cancellables)
37+
/// }
38+
/// }
39+
/// ```
40+
///
41+
/// - Parameters:
42+
/// - state: The state of an alert that can be shown to the user.
43+
/// - send: A function that wraps an alert action in the view store's action type.
44+
public convenience init<Action>(
45+
state: AlertState<Action>,
46+
send: @escaping (Action) -> Void
47+
) {
48+
self.init(
49+
title: String(state: state.title),
50+
message: state.message.map { String(state: $0) },
51+
preferredStyle: .alert)
52+
53+
if let primaryButton = state.primaryButton {
54+
self.addAction(primaryButton.toUIAlertAction(send: send))
55+
}
56+
57+
if let secondaryButton = state.secondaryButton {
58+
self.addAction(secondaryButton.toUIAlertAction(send: send))
59+
}
60+
}
61+
62+
/// Creates a `UIAlertController` from `ActionSheetState`.
63+
///
64+
/// - Parameters:
65+
/// - state: The state of an action sheet that can be shown to the user.
66+
/// - send: A function that wraps a alert action in the view store's action type.
67+
public convenience init<Action>(
68+
state: ActionSheetState<Action>, send: @escaping (Action) -> Void
69+
) {
70+
self.init(
71+
title: String(state: state.title),
72+
message: state.message.map { String(state: $0) },
73+
preferredStyle: .actionSheet)
74+
75+
state.buttons.forEach { button in
76+
self.addAction(button.toUIAlertAction(send: send))
77+
}
78+
}
79+
}
80+
81+
@available(iOS 13, *)
82+
@available(macCatalyst 13, *)
83+
@available(macOS, unavailable)
84+
@available(tvOS 13, *)
85+
@available(watchOS, unavailable)
86+
extension AlertState.Button {
87+
func toUIAlertAction(send: @escaping (Action) -> Void) -> UIAlertAction {
88+
let action = {
89+
switch self.action?.type {
90+
case .none:
91+
return
92+
case let .some(.send(action)),
93+
let .some(.animatedSend(action, animation: _)): // Doesn't support animation in UIKit
94+
send(action)
95+
}
96+
}
97+
switch self.type {
98+
case let .cancel(.some(title)):
99+
return UIAlertAction(title: String(state: title), style: .cancel, handler: { _ in action() })
100+
case .cancel(.none):
101+
return UIAlertAction(title: nil, style: .cancel, handler: { _ in action() })
102+
case let .default(title):
103+
return UIAlertAction(title: String(state: title), style: .default, handler: { _ in action() })
104+
case let .destructive(title):
105+
return UIAlertAction(
106+
title: String(state: title), style: .destructive, handler: { _ in action() })
107+
}
108+
}
109+
}
110+
#endif

0 commit comments

Comments
 (0)