AnyViewGraphNode.swift 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /// A type-erased ``ViewGraphNode``. Used by implementations of ``ViewGraphNodeChildren``
  2. /// to avoid leaking the selected backend into ``View`` implementations (which would be
  3. /// an annoying complexity for users of the library and it worth the slight sacrifice
  4. /// in performance and strong-typing). The user never sees such type-erased wrappers.
  5. @MainActor
  6. public class AnyViewGraphNode<NodeView: View> {
  7. /// The node getting wrapped.
  8. public var node: Any
  9. /// The node's widget (type-erased).
  10. public var widget: AnyWidget {
  11. _getWidget()
  12. }
  13. /// The node's last proposed size.
  14. public var lastProposedSize: ProposedViewSize {
  15. _getLastProposedSize()
  16. }
  17. /// The node's type-erased layout computing method.
  18. private var _computeLayoutWithNewView:
  19. (
  20. _ newView: NodeView?,
  21. _ proposedSize: ProposedViewSize,
  22. _ environment: EnvironmentValues
  23. ) -> ViewLayoutResult
  24. /// The node's type-erased commit method.
  25. private var _commit: () -> ViewLayoutResult
  26. /// The type-erased getter for the node's widget.
  27. private var _getWidget: () -> AnyWidget
  28. /// The type-erased getter for the node's view.
  29. private var _getNodeView: () -> NodeView
  30. /// The type-erased getter for the node's children.
  31. private var _getNodeChildren: () -> any ViewGraphNodeChildren
  32. /// The type-erased getter for the node's underlying erased backend.
  33. private var _getBackend: () -> any BaseAppBackend
  34. /// The type-erased getter for the node's last proposed size.
  35. private var _getLastProposedSize: () -> ProposedViewSize
  36. /// Type-erases a view graph node.
  37. public init<Backend: BaseAppBackend>(_ node: ViewGraphNode<NodeView, Backend>) {
  38. self.node = node
  39. _computeLayoutWithNewView = node.computeLayout(with:proposedSize:environment:)
  40. _commit = node.commit
  41. _getWidget = {
  42. AnyWidget(node.widget)
  43. }
  44. _getNodeView = {
  45. node.view
  46. }
  47. _getNodeChildren = {
  48. node.children
  49. }
  50. _getBackend = {
  51. node.backend
  52. }
  53. _getLastProposedSize = {
  54. node.lastProposedSize
  55. }
  56. }
  57. /// Creates a new view graph node and immediately type-erases it.
  58. public convenience init<Backend: BaseAppBackend>(
  59. for view: NodeView,
  60. backend: Backend,
  61. snapshot: ViewGraphSnapshotter.NodeSnapshot? = nil,
  62. environment: EnvironmentValues
  63. ) {
  64. self.init(
  65. ViewGraphNode(
  66. for: view,
  67. backend: backend,
  68. snapshot: snapshot,
  69. environment: environment
  70. )
  71. )
  72. }
  73. /// Computes a view's layout. Propagates to the view's children unless
  74. /// the given size proposal already has a cached result.
  75. ///
  76. /// - Parameters:
  77. /// - newView: The recomputed view.
  78. /// - proposedSize: The view's proposed size.
  79. /// - environment: The current environment.
  80. /// - Returns: The result of computing the view's layout.
  81. public func computeLayout(
  82. with newView: NodeView?,
  83. proposedSize: ProposedViewSize,
  84. environment: EnvironmentValues
  85. ) -> ViewLayoutResult {
  86. _computeLayoutWithNewView(newView, proposedSize, environment)
  87. }
  88. /// Commits the view's most recently computed layout. Propagates to the
  89. /// view's children. Also commits any view state changes.
  90. public func commit() -> ViewLayoutResult {
  91. _commit()
  92. }
  93. /// Gets the node's wrapped view.
  94. ///
  95. /// - Returns: The node's wrapped view.
  96. public func getView() -> NodeView {
  97. _getNodeView()
  98. }
  99. /// Gets the node's children.
  100. ///
  101. /// - Returns: The node's children.
  102. public func getChildren() -> any ViewGraphNodeChildren {
  103. _getNodeChildren()
  104. }
  105. /// Gets the node's backend.
  106. ///
  107. /// - Returns: The node's backend.
  108. public func getBackend() -> any BaseAppBackend {
  109. _getBackend()
  110. }
  111. /// Converts the node back to its original type. Crashes if the requested backend doesn't
  112. /// match the node's original backend.
  113. public func concreteNode<Backend: BaseAppBackend>(
  114. for backend: Backend.Type
  115. ) -> ViewGraphNode<NodeView, Backend> {
  116. guard let node = node as? ViewGraphNode<NodeView, Backend> else {
  117. fatalError("AnyViewGraphNode used with incompatible backend \(backend)")
  118. }
  119. return node
  120. }
  121. }