AlertScene.swift 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. /// A scene that shows a standalone alert.
  2. ///
  3. /// The exact behavior of the alert is backend-dependent, but it typically
  4. /// shows up as an application modal, or attaches itself to the app's main
  5. /// window.
  6. public struct AlertScene: Scene {
  7. public typealias Node = AlertSceneNode
  8. var title: String
  9. @Binding var isPresented: Bool
  10. var actions: [AlertAction]
  11. /// Creates an alert scene.
  12. ///
  13. /// The exact behavior of the alert is backend-dependent, but it typically
  14. /// shows up as an application modal, or attaches itself to the app's main
  15. /// window.
  16. ///
  17. /// - Parameters:
  18. /// - title: The alert's title.
  19. /// - isPresented: A binding to a `Bool` that controls whether the alert
  20. /// is presented.
  21. /// - actions: The alert's actions.
  22. public init(
  23. _ title: String,
  24. isPresented: Binding<Bool>,
  25. @AlertActionsBuilder actions: () -> [AlertAction]
  26. ) {
  27. self.title = title
  28. self._isPresented = isPresented
  29. self.actions = actions()
  30. }
  31. }
  32. /// The scene graph node for ``AlertScene``.
  33. public final class AlertSceneNode: SceneGraphNode {
  34. public typealias NodeScene = AlertScene
  35. private var scene: AlertScene
  36. private var alert: Any?
  37. public init<Backend: BaseAppBackend>(
  38. from scene: AlertScene,
  39. backend: Backend,
  40. environment: EnvironmentValues
  41. ) {
  42. self.scene = scene
  43. }
  44. public func updateNode(
  45. _ newScene: NodeScene?,
  46. environment: EnvironmentValues
  47. ) -> SceneNodeUpdateResult {
  48. if let newScene {
  49. self.scene = newScene
  50. }
  51. return .leafScene()
  52. }
  53. public func update<Backend: BaseAppBackend>(
  54. backend: Backend,
  55. environment: EnvironmentValues
  56. ) {
  57. func castBackend_inner<NewBackend: BaseAppBackend & BackendFeatures.Alerts>(_ backend: NewBackend) {
  58. if scene.isPresented, alert == nil {
  59. let alert = backend.createAlert()
  60. backend.updateAlert(
  61. alert,
  62. title: scene.title,
  63. actionLabels: scene.actions.map(\.label),
  64. environment: environment
  65. )
  66. backend.showAlert(alert, window: nil) { responseId in
  67. self.alert = nil
  68. self.scene.isPresented = false
  69. self.scene.actions[responseId].action()
  70. }
  71. self.alert = alert
  72. } else if !scene.isPresented, let alert {
  73. backend.dismissAlert(alert as! NewBackend.Alert, window: nil)
  74. self.alert = nil
  75. }
  76. }
  77. guard let castedBackend = backend as? any (BaseAppBackend & BackendFeatures.Alerts) else {
  78. fatalError("Backend does not implement BackendFeatures.Alerts")
  79. }
  80. castBackend_inner(castedBackend)
  81. }
  82. }