diff --git a/Package.resolved b/Package.resolved index 20bec5e..4a4383d 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,23 +1,24 @@ { + "originHash" : "d9e897732f60af4412110707787651b74e68d6f3e1b560c546e4b10fc8f816e7", "pins" : [ { "identity" : "descriptors", "kind" : "remoteSourceControl", - "location" : "https://github.com/FluidGroup/Descriptors", + "location" : "https://github.com/ntnmrndn/Descriptors", "state" : { - "revision" : "174dbbaff40b455614b62ea3ed16771adde25e65", - "version" : "0.2.2" + "branch" : "antoine/swift6", + "revision" : "8f0c6b119cb15e09155460576773f1151c96ce62" } }, { "identity" : "texture", "kind" : "remoteSourceControl", - "location" : "https://github.com/FluidGroup/Texture.git", + "location" : "https://github.com/ntnmrndn/Texture.git", "state" : { - "revision" : "68df47f0d26522da76b06f22e9a97e4d4ab58dad", - "version" : "3.0.2" + "branch" : "antoine/swift6", + "revision" : "2c945a0641d39628f06d21ef7b7f4fb64119812f" } } ], - "version" : 2 + "version" : 3 } diff --git a/Package.swift b/Package.swift index 846c451..bb386bb 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.6 +// swift-tools-version:6.0 import PackageDescription let package = Package( @@ -10,12 +10,12 @@ let package = Package( .library(name: "TextureSwiftSupport", targets: ["TextureSwiftSupport"]), ], dependencies: [ - .package(url: "https://github.com/FluidGroup/Texture.git", from: "3.0.2"), - .package(url: "https://github.com/FluidGroup/Descriptors", from: "0.2.0"), + .package(url: "https://github.com/ntnmrndn/Texture.git", branch: "antoine/swift6"), + .package(url: "https://github.com/ntnmrndn/Descriptors", branch: "antoine/swift6"), ], targets: [ .target( - name: "TextureSwiftSupport", + name: "TextureSwiftSupport", dependencies: [ .product(name: "AsyncDisplayKit", package: "Texture"), .product(name: "Descriptors", package: "Descriptors") @@ -23,5 +23,5 @@ let package = Package( path: "Sources" ), ], - swiftLanguageVersions: [.v5] + swiftLanguageModes: [.v6] ) diff --git a/Sources/Components/Compositions/AnyDisplayNode.swift b/Sources/Components/Compositions/AnyDisplayNode.swift index 1b1864c..a9e3016 100644 --- a/Sources/Components/Compositions/AnyDisplayNode.swift +++ b/Sources/Components/Compositions/AnyDisplayNode.swift @@ -106,7 +106,7 @@ open class AnyDisplayNode: SafeAreaDisplayNode { } @available(*, unavailable) - open override func onDidLoad(_ body: @escaping ASDisplayNodeDidLoadBlock) { + open override func onDidLoad(_ body: @escaping ASDisplayNodeDidLoadBlock) { super.onDidLoad(body) } diff --git a/Sources/Components/Compositions/HighlightCellNode.swift b/Sources/Components/Compositions/HighlightCellNode.swift index f9bbbcc..b126efa 100644 --- a/Sources/Components/Compositions/HighlightCellNode.swift +++ b/Sources/Components/Compositions/HighlightCellNode.swift @@ -106,6 +106,7 @@ open class HighlightCellNode: NamedDisplayCellNodeBase { } } + @MainActor open override var isHighlighted: Bool { didSet { highlightHandler?(isHighlighted, self.view, animationTargetNode.view) diff --git a/Sources/Components/Compositions/InteractiveNode.swift b/Sources/Components/Compositions/InteractiveNode.swift index 4c84a7d..bec5cb7 100644 --- a/Sources/Components/Compositions/InteractiveNode.swift +++ b/Sources/Components/Compositions/InteractiveNode.swift @@ -151,6 +151,7 @@ open class InteractiveNode: NamedDisplayControlNodeBase { } } + @MainActor open override var isHighlighted: Bool { didSet { highlightHandler?(isHighlighted, self.view, animationTargetNode.view) diff --git a/Sources/Components/Compositions/OnAppearNode.swift b/Sources/Components/Compositions/OnAppearNode.swift index 4e836fa..8734774 100644 --- a/Sources/Components/Compositions/OnAppearNode.swift +++ b/Sources/Components/Compositions/OnAppearNode.swift @@ -73,9 +73,13 @@ public final class OnAppearNode: NamedDisplayCellNodeBas state = .init() - bottomRightTiledLayerNode = .init(wrappedView: { .init() }) - topLeftTiledLayerNode = .init(wrappedView: { .init() }) - + bottomRightTiledLayerNode = .init(wrappedView: { + .init() + }) + topLeftTiledLayerNode = .init(wrappedView: { + .init() + }) + setNeedsLayout() bottomRightTiledLayerNode!.wrappedView.setOnDraw { [weak self] in @@ -214,24 +218,31 @@ private final class TiledLayerView: UIView { return view } - func setOnDraw(_ closure: @escaping @MainActor () -> Void) { + func setOnDraw(_ closure: @Sendable @escaping @MainActor () -> Void) { (layer as! TiledLayer).onDraw = closure } } private final class TiledLayer: CATiledLayer { - var onDraw: () -> Void = {} + var onDraw: @MainActor @Sendable () -> Void = {} override class func fadeDuration() -> CFTimeInterval { 0 } override func draw(in ctx: CGContext) { + let onDraw = self.onDraw if Thread.isMainThread { - onDraw() + MainActor.assumeIsolated { + onDraw() + } } else { - DispatchQueue.main.async(execute: onDraw) + DispatchQueue.main.async(execute: { + MainActor.assumeIsolated { + onDraw() + } + }) } } } diff --git a/Sources/Components/Compositions/ShapeLayerNode.swift b/Sources/Components/Compositions/ShapeLayerNode.swift index a96cc6e..946e7f0 100644 --- a/Sources/Components/Compositions/ShapeLayerNode.swift +++ b/Sources/Components/Compositions/ShapeLayerNode.swift @@ -26,8 +26,8 @@ fileprivate final class BackingShapeLayerNode : ASDisplayNode { } /// A node that displays shape with CAShapeLayer -public final class ShapeLayerNode : ASDisplayNode, ShapeDisplaying { - +public final class ShapeLayerNode : ASDisplayNode, MainActorShapeDisplaying, @unchecked Sendable { + private let backingNode = BackingShapeLayerNode() private let updateClosure: Update @@ -38,54 +38,87 @@ public final class ShapeLayerNode : ASDisplayNode, ShapeDisplaying { } } + public init(update: @escaping Update) { + self.updateClosure = update + super.init() + backgroundColor = .clear + backingNode.backgroundColor = .clear + backingNode.isLayerBacked = true + automaticallyManagesSubnodes = true + } + + /// Warning: shape\* values will actually be set at didLoad + public convenience init ( + update: @escaping Update, + shapeFillColor: UIColor = .clear, + shapeStrokeColor: UIColor = .clear, + shapeLineWidth: CGFloat = 0.0 + ) { + self.init(update: update) + backgroundColor = .clear + backingNode.backgroundColor = .clear + backingNode.isLayerBacked = true + automaticallyManagesSubnodes = true + self.onDidLoad({ + let shapeLayerNode = ($0 as! ShapeLayerNode) + shapeLayerNode.shapeFillColor = shapeFillColor + shapeLayerNode.shapeStrokeColor = shapeStrokeColor + shapeLayerNode.shapeLineWidth = shapeLineWidth + }) + } + public override var supportsLayerBacking: Bool { return true } - - public var shapeLayer: CAShapeLayer { + + @MainActor + /// Beware, direct access to lineWidth is not supported here when using usesInnerBorder, otherwise access should be safe + public var unsafeShapeLayer: CAShapeLayer { backingNode.layer } - - // To be thread-safe, using stored property + + /// cache value for bg thread access + private var _shapeLineWidth: CGFloat = .zero + + @MainActor public var shapeLineWidth: CGFloat = 0 { didSet { + _shapeLineWidth = shapeLineWidth backingNode.layer.lineWidth = shapeLineWidth setNeedsLayout() } } - + + @MainActor public var shapeStrokeColor: UIColor? { get { return backingNode.layer.strokeColor.map { UIColor(cgColor: $0) } } set { - ASPerformBlockOnMainThread { - CATransaction.begin() - CATransaction.setDisableActions(true) - defer { - CATransaction.commit() - } - self.backingNode.layer.strokeColor = newValue?.cgColor + CATransaction.begin() + CATransaction.setDisableActions(true) + defer { + CATransaction.commit() } + self.backingNode.layer.strokeColor = newValue?.cgColor } } - + + @MainActor public var shapeFillColor: UIColor? { get { return backingNode.layer.fillColor.map { UIColor(cgColor: $0) } } set { - ASPerformBlockOnMainThread { - CATransaction.begin() - CATransaction.setDisableActions(true) - defer { - CATransaction.commit() - } - self.backingNode.layer.fillColor = newValue?.cgColor + CATransaction.begin() + CATransaction.setDisableActions(true) + defer { + CATransaction.commit() } + self.backingNode.layer.fillColor = newValue?.cgColor } } - + public override func layout() { super.layout() CATransaction.begin() @@ -95,41 +128,32 @@ public final class ShapeLayerNode : ASDisplayNode, ShapeDisplaying { } backingNode.layer.path = updateClosure(backingNode.bounds).cgPath } - + public override var frame: CGRect { didSet { ASPerformBlockOnMainThread { - CATransaction.begin() - CATransaction.setDisableActions(true) - defer { - CATransaction.commit() + MainActor.assumeIsolated { + CATransaction.begin() + CATransaction.setDisableActions(true) + defer { + CATransaction.commit() + } + self.backingNode.layer.path = self.updateClosure(self.backingNode.bounds).cgPath } - self.backingNode.layer.path = self.updateClosure(self.backingNode.bounds).cgPath } } } - public init( - update: @escaping Update - ) { - self.updateClosure = update - super.init() - backgroundColor = .clear - backingNode.backgroundColor = .clear - backingNode.isLayerBacked = true - automaticallyManagesSubnodes = true - } - public override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { if usesInnerBorder { return ASWrapperLayoutSpec( layoutElement: ASInsetLayoutSpec( insets: .init( - top: shapeLineWidth / 2, - left: shapeLineWidth / 2, - bottom: shapeLineWidth / 2, - right: shapeLineWidth / 2 + top: _shapeLineWidth / 2, + left: _shapeLineWidth / 2, + bottom: _shapeLineWidth / 2, + right: _shapeLineWidth / 2 ), child: backingNode ) diff --git a/Sources/Components/Compositions/StyledEdgeNode.swift b/Sources/Components/Compositions/StyledEdgeNode.swift index a42df5f..810ce63 100644 --- a/Sources/Components/Compositions/StyledEdgeNode.swift +++ b/Sources/Components/Compositions/StyledEdgeNode.swift @@ -192,6 +192,7 @@ public class StyledEdgeNode: NamedDisplayNodeBase { } + @MainActor public override func didLoad() { super.didLoad() @@ -199,57 +200,60 @@ public class StyledEdgeNode: NamedDisplayNodeBase { updateBorder() updateStrategy() - self.borderNode.shapeLayer.fillRule = .evenOdd + self.borderNode.unsafeShapeLayer.fillRule = .evenOdd } private func updateBorder() { ASPerformBlockOnMainThread { - CATransaction.begin() - CATransaction.setDisableActions(true) - defer { - CATransaction.commit() + MainActor.assumeIsolated { + CATransaction.begin() + CATransaction.setDisableActions(true) + defer { + CATransaction.commit() + } + + if let border = self.border { + self.borderNode.shapeFillColor = .clear + self.borderNode.shapeStrokeColor = border.color + self.borderNode.shapeLineWidth = border.width * 2 // to get inner border + self.borderNode.isHidden = false + } else { + self.borderNode.isHidden = true + } + + self.setNeedsLayout() } - - if let border = self.border { - self.borderNode.shapeFillColor = .clear - self.borderNode.shapeStrokeColor = border.color - self.borderNode.shapeLineWidth = border.width * 2 // to get inner border - self.borderNode.isHidden = false - } else { - self.borderNode.isHidden = true - } - - self.setNeedsLayout() } } private func updateStrategy() { ASPerformBlockOnMainThread { + MainActor.assumeIsolated { - CATransaction.begin() - CATransaction.setDisableActions(true) - defer { - CATransaction.commit() - } + CATransaction.begin() + CATransaction.setDisableActions(true) + defer { + CATransaction.commit() + } - switch self.cornerRoundingStrategy { - case .clip(let assimilationColor): + switch self.cornerRoundingStrategy { + case .clip(let assimilationColor): - self.clipNode.shapeFillColor = assimilationColor + self.clipNode.shapeFillColor = assimilationColor - self.isOpaque = true + self.isOpaque = true - case .mask: + case .mask: - self.isOpaque = false + self.isOpaque = false - } + } - self.setNeedsLayout() + self.setNeedsLayout() + } } - } public override func layoutDidFinish() { diff --git a/Sources/Components/Compositions/ViewNode.swift b/Sources/Components/Compositions/ViewNode.swift index 9c7f4fb..815fe8d 100644 --- a/Sources/Components/Compositions/ViewNode.swift +++ b/Sources/Components/Compositions/ViewNode.swift @@ -24,15 +24,16 @@ import Foundation /** A display node that backed by a custom view. */ -open class ViewNode: NamedDisplayNodeBase { +open class ViewNode: NamedDisplayNodeBase, @unchecked Sendable { + @MainActor open var wrappedView: V { assert(Thread.isMainThread) return view as! V } public init( - wrappedView: @escaping () -> V + wrappedView: @Sendable @MainActor @escaping () -> V ) { super.init() setViewBlock { diff --git a/Sources/Components/Containers/StackScrollNode.swift b/Sources/Components/Containers/StackScrollNode.swift index 0beedbc..a1c6eed 100644 --- a/Sources/Components/Containers/StackScrollNode.swift +++ b/Sources/Components/Containers/StackScrollNode.swift @@ -4,25 +4,30 @@ import Foundation import AsyncDisplayKit /// Backing Component is ASCollectionNode -open class StackScrollNode : NamedDisplayNodeBase, ASCollectionDelegate, ASCollectionDataSource { +open class StackScrollNode: + NamedDisplayNodeBase, + @unchecked Sendable { public final var onScrollViewDidScroll: (UIScrollView) -> Void = { _ in } open var shouldWaitUntilAllUpdatesAreCommitted: Bool = false + @MainActor open var isScrollEnabled: Bool { get { - return collectionNode.view.isScrollEnabled + scrollView.isScrollEnabled } set { - collectionNode.view.isScrollEnabled = newValue + scrollView.isScrollEnabled = newValue } } + @MainActor open var scrollView: UIScrollView { return collectionNode.view } + @MainActor open var collectionViewLayout: UICollectionViewFlowLayout { return collectionNode.view.collectionViewLayout as! UICollectionViewFlowLayout } @@ -32,14 +37,17 @@ open class StackScrollNode : NamedDisplayNodeBase, ASCollectionDelegate, ASColle /// It should not be accessed unless there is special. internal let collectionNode: ASCollectionNode + @MainActor public init(layout: UICollectionViewFlowLayout) { collectionNode = ASCollectionNode(collectionViewLayout: layout) collectionNode.backgroundColor = .clear + self.collectionViewLayoutScrollDirection = layout.scrollDirection super.init() } + @MainActor public convenience init( scrollDirection: UICollectionView.ScrollDirection, spacing: CGFloat, @@ -54,16 +62,16 @@ open class StackScrollNode : NamedDisplayNodeBase, ASCollectionDelegate, ASColle self.init(layout: layout) } - public override convenience init() { - + @MainActor + static func makeCollectionViewFlowLayoutNode() -> StackScrollNode { let layout = UICollectionViewFlowLayout() layout.minimumInteritemSpacing = 0 layout.minimumLineSpacing = 0 layout.sectionInset = .zero - - self.init(layout: layout) + return .init(layout: layout) } + @MainActor open func append(nodes: [ASCellNode]) { self.nodes += nodes @@ -74,6 +82,7 @@ open class StackScrollNode : NamedDisplayNodeBase, ASCollectionDelegate, ASColle } } + @MainActor open func removeAll() { self.nodes = [] @@ -83,6 +92,7 @@ open class StackScrollNode : NamedDisplayNodeBase, ASCollectionDelegate, ASColle } } + @MainActor open func replaceAll(nodes: [ASCellNode]) { self.nodes = nodes @@ -118,11 +128,14 @@ open class StackScrollNode : NamedDisplayNodeBase, ASCollectionDelegate, ASColle return ASWrapperLayoutSpec(layoutElement: collectionNode) } + + // Cache the value for BG thread access + private var collectionViewLayoutScrollDirection: UICollectionView.ScrollDirection // MARK: - ASCollectionDelegate public func collectionNode(_ collectionNode: ASCollectionNode, constrainedSizeForItemAt indexPath: IndexPath) -> ASSizeRange { - switch collectionViewLayout.scrollDirection { + switch collectionViewLayoutScrollDirection { case .vertical: return ASSizeRange( @@ -141,24 +154,27 @@ open class StackScrollNode : NamedDisplayNodeBase, ASCollectionDelegate, ASColle fatalError() } } +} + +extension StackScrollNode: ASCollectionDelegate { + // MARK: - UIScrollViewDelegate + public func scrollViewDidScroll(_ scrollView: UIScrollView) { + onScrollViewDidScroll(scrollView) + } +} +extension StackScrollNode: ASCollectionDataSource { // MARK: - ASCollectionDataSource - open var numberOfSections: Int { + @objc open var numberOfSections: Int { return 1 } - public func collectionNode(_ collectionNode: ASCollectionNode, numberOfItemsInSection section: Int) -> Int { - return nodes.count + @objc public func collectionNode(_ collectionNode: ASCollectionNode, numberOfItemsInSection section: Int) -> Int { + nodes.count } - public func collectionNode(_ collectionNode: ASCollectionNode, nodeForItemAt indexPath: IndexPath) -> ASCellNode { - return nodes[indexPath.item] - } - - // MARK: - UIScrollViewDelegate - public func scrollViewDidScroll(_ scrollView: UIScrollView) { - onScrollViewDidScroll(scrollView) + @objc public func collectionNode(_ collectionNode: ASCollectionNode, nodeForItemAt indexPath: IndexPath) -> ASCellNode { + nodes[indexPath.item] } } - diff --git a/Sources/Components/Elements/GradientLayerNode.swift b/Sources/Components/Elements/GradientLayerNode.swift index f3966a4..3c1ddc2 100644 --- a/Sources/Components/Elements/GradientLayerNode.swift +++ b/Sources/Components/Elements/GradientLayerNode.swift @@ -31,14 +31,15 @@ fileprivate final class GradientLayerView: UIView { @available(*, deprecated, renamed: "GradientLayerNode") public typealias GradientNode = GradientLayerNode -open class GradientLayerNode : ASDisplayNode { +open class GradientLayerNode : ASDisplayNode, @unchecked Sendable { private var usingDescriptor: LinearGradientDescriptor? open override var supportsLayerBacking: Bool { return false } - + + @MainActor public var gradientLayer: CAGradientLayer { view.layer as! CAGradientLayer } @@ -56,6 +57,7 @@ open class GradientLayerNode : ASDisplayNode { backgroundColor = .clear } + @MainActor open override func didLoad() { super.didLoad() @@ -65,6 +67,7 @@ open class GradientLayerNode : ASDisplayNode { } + @MainActor open func setDescriptor(descriptor: LinearGradientDescriptor) { lock() @@ -78,7 +81,9 @@ open class GradientLayerNode : ASDisplayNode { if self.isNodeLoaded { ASPerformBlockOnMainThread { - descriptor.apply(to: (self.view.layer as! CAGradientLayer)) + MainActor.assumeIsolated { + descriptor.apply(to: (self.view.layer as! CAGradientLayer)) + } } } else { // will be applied on didLoad diff --git a/Sources/Components/Tools/NamedDisplayCellNodeBase.swift b/Sources/Components/Tools/NamedDisplayCellNodeBase.swift index 2b67497..45402a0 100644 --- a/Sources/Components/Tools/NamedDisplayCellNodeBase.swift +++ b/Sources/Components/Tools/NamedDisplayCellNodeBase.swift @@ -30,8 +30,8 @@ fileprivate let queue = DispatchQueue.global() /// It helps to find source code from Reveal. /// /// - Author: TetureSwiftSupport -open class NamedDisplayCellNodeBase: ASCellNode { - +open class NamedDisplayCellNodeBase: ASCellNode, @unchecked Sendable { + private var __actionHandlers: [(NamedDisplayCellNodeBase, DisplayNodeAction) -> Void] = [] @MainActor diff --git a/Sources/Components/Tools/NamedDisplayNodeBase.swift b/Sources/Components/Tools/NamedDisplayNodeBase.swift index 535c04f..46da24b 100644 --- a/Sources/Components/Tools/NamedDisplayNodeBase.swift +++ b/Sources/Components/Tools/NamedDisplayNodeBase.swift @@ -34,8 +34,8 @@ public enum DisplayNodeAction { /// It helps to find source code from Reveal. /// /// - Author: TetureSwiftSupport -open class NamedDisplayNodeBase: ASDisplayNode { - +open class NamedDisplayNodeBase: ASDisplayNode, @unchecked Sendable { + private var __actionHandlers: [@MainActor (NamedDisplayNodeBase, DisplayNodeAction) -> Void] = [] @MainActor @@ -71,7 +71,6 @@ open class NamedDisplayNodeBase: ASDisplayNode { return self } - @preconcurrency @MainActor private func propagate(action: DisplayNodeAction) { for handler in __actionHandlers { @@ -88,8 +87,8 @@ open class NamedDisplayNodeBase: ASDisplayNode { /// It helps to find source code from Reveal. /// /// - Author: TetureSwiftSupport -open class NamedDisplayControlNodeBase: ASControlNode { - +open class NamedDisplayControlNodeBase: ASControlNode, @unchecked Sendable { + private var __actionHandlers: [@MainActor (NamedDisplayControlNodeBase, DisplayNodeAction) -> Void] = [] @MainActor @@ -113,7 +112,7 @@ open class NamedDisplayControlNodeBase: ASControlNode { - Warning: Non-atomic */ @discardableResult - public func addNodeActionHandler(_ handler: @escaping (Self, DisplayNodeAction) -> Void) -> Self { + public func addNodeActionHandler(_ handler: @escaping @MainActor (Self, DisplayNodeAction) -> Void) -> Self { __actionHandlers.append { node, action in guard let node = node as? Self else { assertionFailure() diff --git a/Sources/Components/Tools/SafeAreaDisplayNode.swift b/Sources/Components/Tools/SafeAreaDisplayNode.swift index cd696fe..e6020d3 100644 --- a/Sources/Components/Tools/SafeAreaDisplayNode.swift +++ b/Sources/Components/Tools/SafeAreaDisplayNode.swift @@ -28,8 +28,8 @@ import AsyncDisplayKit - Author: TetureSwiftSupport */ -open class SafeAreaDisplayNode: NamedDisplayNodeBase { - +open class SafeAreaDisplayNode: NamedDisplayNodeBase, @unchecked Sendable { + public var throughsTouches: Bool = false // Thread safe @@ -40,7 +40,8 @@ open class SafeAreaDisplayNode: NamedDisplayNodeBase { automaticallyRelayoutOnSafeAreaChanges = true automaticallyRelayoutOnLayoutMarginsChanges = true } - + + open override func safeAreaInsetsDidChange() { super.safeAreaInsetsDidChange() capturedSafeAreaInsets = safeAreaInsets diff --git a/Sources/Experiments/NodeFactory.swift b/Sources/Experiments/NodeFactory.swift index b36b04b..6ef401d 100644 --- a/Sources/Experiments/NodeFactory.swift +++ b/Sources/Experiments/NodeFactory.swift @@ -2,7 +2,7 @@ import Foundation import AsyncDisplayKit -fileprivate var _storageKey: Void? +nonisolated(unsafe) fileprivate var _storageKey: Void? extension ASDisplayNode { diff --git a/Sources/LayoutSpecBuilders/Layout/SwitchCaseLayout.swift b/Sources/LayoutSpecBuilders/Layout/SwitchCaseLayout.swift index 99693d3..a6502fd 100644 --- a/Sources/LayoutSpecBuilders/Layout/SwitchCaseLayout.swift +++ b/Sources/LayoutSpecBuilders/Layout/SwitchCaseLayout.swift @@ -207,7 +207,7 @@ public struct ConditionInspector: _ASLayoutElementType { fileprivate let emptyLayout = ASLayoutSpec() -final class _CaseLayoutSpec: ASLayoutSpec { +final class _CaseLayoutSpec: ASLayoutSpec, @unchecked Sendable { let condition: LayoutCondition diff --git a/Sources/LayoutSpecBuilders/Modifiers.swift b/Sources/LayoutSpecBuilders/Modifiers.swift index f8bfef5..caf1593 100644 --- a/Sources/LayoutSpecBuilders/Modifiers.swift +++ b/Sources/LayoutSpecBuilders/Modifiers.swift @@ -10,15 +10,15 @@ import Foundation import UIKit import AsyncDisplayKit -public enum Edge: Int8, CaseIterable { - +public enum Edge: Int8, CaseIterable, Sendable { + case top = 0 case left = 1 case bottom = 2 case right = 3 - public struct Set: OptionSet { - + public struct Set: OptionSet, Sendable { + public var rawValue: Int8 public var isEmpty: Bool { rawValue == 0