From f4c380f566e6daf6438b7fcd40c871f9e8954600 Mon Sep 17 00:00:00 2001 From: Antoine Marandon Date: Mon, 25 Nov 2024 17:07:26 +0900 Subject: [PATCH 1/8] Swift 6 support --- Package.resolved | 13 ++-- Package.swift | 8 +-- .../Compositions/HighlightCellNode.swift | 1 + .../Compositions/InteractiveNode.swift | 1 + .../Compositions/OnAppearNode.swift | 30 ++++++--- .../Compositions/ShapeLayerNode.swift | 62 ++++++++++++------- .../Compositions/StyledEdgeNode.swift | 61 +++++++++--------- .../Components/Compositions/ViewNode.swift | 8 ++- .../Containers/StackScrollNode.swift | 24 ++++--- .../Elements/GradientLayerNode.swift | 10 ++- .../Tools/NamedDisplayCellNodeBase.swift | 4 +- .../Tools/NamedDisplayNodeBase.swift | 10 +-- .../Tools/SafeAreaDisplayNode.swift | 7 ++- Sources/Experiments/NodeFactory.swift | 2 +- .../Layout/SwitchCaseLayout.swift | 2 +- Sources/LayoutSpecBuilders/Modifiers.swift | 8 +-- 16 files changed, 151 insertions(+), 100 deletions(-) diff --git a/Package.resolved b/Package.resolved index 20bec5e..41fb4f8 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,23 +1,24 @@ { + "originHash" : "f84a1666194bcf450224fdc7fff73c480bbd9e6e0e9fcba6fe117a25c305682d", "pins" : [ { "identity" : "descriptors", "kind" : "remoteSourceControl", "location" : "https://github.com/FluidGroup/Descriptors", "state" : { - "revision" : "174dbbaff40b455614b62ea3ed16771adde25e65", - "version" : "0.2.2" + "revision" : "1ec4fcd01b7e3a6df9dfafcca8155e297a7a1d9f", + "version" : "0.3.0" } }, { "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" : "2a8c4b9ca4591229e0017c06083165cd1d3242ab" } } ], - "version" : 2 + "version" : 3 } diff --git a/Package.swift b/Package.swift index 846c451..019b139 100644 --- a/Package.swift +++ b/Package.swift @@ -1,16 +1,16 @@ -// swift-tools-version:5.6 +// swift-tools-version:6.0 import PackageDescription let package = Package( name: "TextureSwiftSupport", platforms: [ - .iOS(.v13), + .iOS(.v15), ], products: [ .library(name: "TextureSwiftSupport", targets: ["TextureSwiftSupport"]), ], dependencies: [ - .package(url: "https://github.com/FluidGroup/Texture.git", from: "3.0.2"), + .package(url: "https://github.com/ntnmrndn/Texture.git", branch: "antoine/swift6"), .package(url: "https://github.com/FluidGroup/Descriptors", from: "0.2.0"), ], targets: [ @@ -23,5 +23,5 @@ let package = Package( path: "Sources" ), ], - swiftLanguageVersions: [.v5] + swiftLanguageModes: [.v6] ) 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..0e1c7c7 100644 --- a/Sources/Components/Compositions/OnAppearNode.swift +++ b/Sources/Components/Compositions/OnAppearNode.swift @@ -73,9 +73,18 @@ public final class OnAppearNode: NamedDisplayCellNodeBas state = .init() - bottomRightTiledLayerNode = .init(wrappedView: { .init() }) - topLeftTiledLayerNode = .init(wrappedView: { .init() }) - + bottomRightTiledLayerNode = .init(wrappedView: { + MainActor.assumeIsolated { + .init() + } + }) + topLeftTiledLayerNode = .init(wrappedView: { + MainActor.assumeIsolated { + // NB, + .init() + } + }) + setNeedsLayout() bottomRightTiledLayerNode!.wrappedView.setOnDraw { [weak self] in @@ -214,24 +223,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..f2aba1a 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, @preconcurrency ShapeDisplaying, @unchecked Sendable { + private let backingNode = BackingShapeLayerNode() private let updateClosure: Update @@ -41,47 +41,61 @@ public final class ShapeLayerNode : ASDisplayNode, ShapeDisplaying { public override var supportsLayerBacking: Bool { return true } - + + @MainActor public var shapeLayer: CAShapeLayer { backingNode.layer } // To be thread-safe, using stored property + /// Should be set on mainThread, TODO: Discuss how to enforce the rule public var shapeLineWidth: CGFloat = 0 { didSet { - backingNode.layer.lineWidth = shapeLineWidth - setNeedsLayout() + MainActor.assumeIsolated { + backingNode.layer.lineWidth = shapeLineWidth + setNeedsLayout() + } } } - + + @MainActor public var shapeStrokeColor: UIColor? { get { return backingNode.layer.strokeColor.map { UIColor(cgColor: $0) } } set { + // Keep it for now since we might have non confirming implementation ASPerformBlockOnMainThread { - CATransaction.begin() - CATransaction.setDisableActions(true) - defer { - CATransaction.commit() + MainActor.assumeIsolated { + + CATransaction.begin() + CATransaction.setDisableActions(true) + defer { + CATransaction.commit() + } + self.backingNode.layer.strokeColor = newValue?.cgColor } - self.backingNode.layer.strokeColor = newValue?.cgColor } } } - + + @MainActor public var shapeFillColor: UIColor? { get { return backingNode.layer.fillColor.map { UIColor(cgColor: $0) } } set { + // Keep it for now since we might have non confirming implementation ASPerformBlockOnMainThread { - CATransaction.begin() - CATransaction.setDisableActions(true) - defer { - CATransaction.commit() + MainActor.assumeIsolated { + + CATransaction.begin() + CATransaction.setDisableActions(true) + defer { + CATransaction.commit() + } + self.backingNode.layer.fillColor = newValue?.cgColor } - self.backingNode.layer.fillColor = newValue?.cgColor } } } @@ -95,16 +109,18 @@ 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 } } } diff --git a/Sources/Components/Compositions/StyledEdgeNode.swift b/Sources/Components/Compositions/StyledEdgeNode.swift index a42df5f..dc95676 100644 --- a/Sources/Components/Compositions/StyledEdgeNode.swift +++ b/Sources/Components/Compositions/StyledEdgeNode.swift @@ -205,51 +205,54 @@ public class StyledEdgeNode: NamedDisplayNodeBase { 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..b5a09fe 100644 --- a/Sources/Components/Compositions/ViewNode.swift +++ b/Sources/Components/Compositions/ViewNode.swift @@ -26,17 +26,21 @@ import Foundation */ open class ViewNode: NamedDisplayNodeBase { + @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 { - wrappedView() + // https://github.com/swiftlang/swift/issues/76099 + MainActor.assumeIsolated { + wrappedView() + } } } } diff --git a/Sources/Components/Containers/StackScrollNode.swift b/Sources/Components/Containers/StackScrollNode.swift index 0beedbc..a98d2be 100644 --- a/Sources/Components/Containers/StackScrollNode.swift +++ b/Sources/Components/Containers/StackScrollNode.swift @@ -4,7 +4,7 @@ import Foundation import AsyncDisplayKit /// Backing Component is ASCollectionNode -open class StackScrollNode : NamedDisplayNodeBase, ASCollectionDelegate, ASCollectionDataSource { +open class StackScrollNode : NamedDisplayNodeBase, ASCollectionDelegate, ASCollectionDataSource, @unchecked Sendable { public final var onScrollViewDidScroll: (UIScrollView) -> Void = { _ in } @@ -54,14 +54,13 @@ 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) } open func append(nodes: [ASCellNode]) { @@ -147,13 +146,18 @@ open class StackScrollNode : NamedDisplayNodeBase, ASCollectionDelegate, ASColle return 1 } - public func collectionNode(_ collectionNode: ASCollectionNode, numberOfItemsInSection section: Int) -> Int { - - return nodes.count + nonisolated public func collectionNode(_ collectionNode: ASCollectionNode, numberOfItemsInSection section: Int) -> Int { + // I'm not sure why self is considered @mainactor here, ... + MainActor.assumeIsolated { + nodes.count + } } - public func collectionNode(_ collectionNode: ASCollectionNode, nodeForItemAt indexPath: IndexPath) -> ASCellNode { - return nodes[indexPath.item] + nonisolated public func collectionNode(_ collectionNode: ASCollectionNode, nodeForItemAt indexPath: IndexPath) -> ASCellNode { + /// See abobe + MainActor.assumeIsolated { + nodes[indexPath.item] + } } // MARK: - UIScrollViewDelegate diff --git a/Sources/Components/Elements/GradientLayerNode.swift b/Sources/Components/Elements/GradientLayerNode.swift index f3966a4..f2f8e60 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 } @@ -65,6 +66,7 @@ open class GradientLayerNode : ASDisplayNode { } + /// why are we using both assert main thread and lock and dispatch ? open func setDescriptor(descriptor: LinearGradientDescriptor) { lock() @@ -78,7 +80,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..7755fcc 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 @@ -88,8 +88,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 +113,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 From 31eb3cbab02970f7c7f21b2d39555d54c6c44c6f Mon Sep 17 00:00:00 2001 From: Antoine Marandon Date: Mon, 25 Nov 2024 17:10:54 +0900 Subject: [PATCH 2/8] fix comment --- Sources/Components/Compositions/OnAppearNode.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Components/Compositions/OnAppearNode.swift b/Sources/Components/Compositions/OnAppearNode.swift index 0e1c7c7..45a153f 100644 --- a/Sources/Components/Compositions/OnAppearNode.swift +++ b/Sources/Components/Compositions/OnAppearNode.swift @@ -80,7 +80,7 @@ public final class OnAppearNode: NamedDisplayCellNodeBas }) topLeftTiledLayerNode = .init(wrappedView: { MainActor.assumeIsolated { - // NB, + // :/ https://github.com/swiftlang/swift/issues/76099 .init() } }) From f417f25b1d42beb5c60b51cce4add2242b837c8b Mon Sep 17 00:00:00 2001 From: Antoine Marandon Date: Wed, 27 Nov 2024 12:20:07 +0900 Subject: [PATCH 3/8] Update Dependency --- Package.resolved | 8 ++++---- Package.swift | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Package.resolved b/Package.resolved index 41fb4f8..ec9b43e 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "f84a1666194bcf450224fdc7fff73c480bbd9e6e0e9fcba6fe117a25c305682d", + "originHash" : "1ec615bb9859848353ee388118e25fe6e7cc4baa377b5045fc273ea94edff5fc", "pins" : [ { "identity" : "descriptors", "kind" : "remoteSourceControl", - "location" : "https://github.com/FluidGroup/Descriptors", + "location" : "https://github.com/ntnmrndn/Descriptors", "state" : { - "revision" : "1ec4fcd01b7e3a6df9dfafcca8155e297a7a1d9f", - "version" : "0.3.0" + "branch" : "antoine/swift6", + "revision" : "f67005499aac61b4458ea536e62351d70b4647d6" } }, { diff --git a/Package.swift b/Package.swift index 019b139..093ce60 100644 --- a/Package.swift +++ b/Package.swift @@ -11,11 +11,11 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/ntnmrndn/Texture.git", branch: "antoine/swift6"), - .package(url: "https://github.com/FluidGroup/Descriptors", from: "0.2.0"), + .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") From 674c237c3ae76c6a6a5a64e3651230aa6c7e808d Mon Sep 17 00:00:00 2001 From: Antoine Marandon Date: Wed, 27 Nov 2024 18:58:02 +0900 Subject: [PATCH 4/8] Update --- Package.resolved | 5 +- .../Compositions/AnyDisplayNode.swift | 2 +- .../Compositions/ShapeLayerNode.swift | 102 ++++++++++-------- .../Compositions/StyledEdgeNode.swift | 3 +- .../Elements/GradientLayerNode.swift | 3 +- .../Tools/NamedDisplayNodeBase.swift | 1 - 6 files changed, 63 insertions(+), 53 deletions(-) diff --git a/Package.resolved b/Package.resolved index ec9b43e..b288bd2 100644 --- a/Package.resolved +++ b/Package.resolved @@ -7,7 +7,7 @@ "location" : "https://github.com/ntnmrndn/Descriptors", "state" : { "branch" : "antoine/swift6", - "revision" : "f67005499aac61b4458ea536e62351d70b4647d6" + "revision" : "b1eb25aa85dbcac3149438c7ac8d13949317ba45" } }, { @@ -16,9 +16,10 @@ "location" : "https://github.com/ntnmrndn/Texture.git", "state" : { "branch" : "antoine/swift6", - "revision" : "2a8c4b9ca4591229e0017c06083165cd1d3242ab" + "revision" : "5bd706009aeee6711656cc993fdb60131c4d04f0" } } ], "version" : 3 } + 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/ShapeLayerNode.swift b/Sources/Components/Compositions/ShapeLayerNode.swift index f2aba1a..fddcf42 100644 --- a/Sources/Components/Compositions/ShapeLayerNode.swift +++ b/Sources/Components/Compositions/ShapeLayerNode.swift @@ -26,7 +26,7 @@ fileprivate final class BackingShapeLayerNode : ASDisplayNode { } /// A node that displays shape with CAShapeLayer -public final class ShapeLayerNode : ASDisplayNode, @preconcurrency ShapeDisplaying, @unchecked Sendable { +public final class ShapeLayerNode : ASDisplayNode, MainActorShapeDisplaying, @unchecked Sendable { private let backingNode = BackingShapeLayerNode() @@ -38,23 +38,54 @@ public final class ShapeLayerNode : ASDisplayNode, @preconcurrency ShapeDisplay } } + 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 } @MainActor - public var shapeLayer: CAShapeLayer { + /// Beware, direct access to lineWidth is not supported here when using usesInnerBorder, otherwise acess should be safe + public var unsafeShapeLayer: CAShapeLayer { backingNode.layer } - - // To be thread-safe, using stored property - /// Should be set on mainThread, TODO: Discuss how to enforce the rule + + /// cache value for bg thread access + private var _shapeLineWidth: CGFloat = .zero + + @MainActor public var shapeLineWidth: CGFloat = 0 { didSet { - MainActor.assumeIsolated { - backingNode.layer.lineWidth = shapeLineWidth - setNeedsLayout() - } + _shapeLineWidth = shapeLineWidth + backingNode.layer.lineWidth = shapeLineWidth + setNeedsLayout() } } @@ -64,18 +95,12 @@ public final class ShapeLayerNode : ASDisplayNode, @preconcurrency ShapeDisplay return backingNode.layer.strokeColor.map { UIColor(cgColor: $0) } } set { - // Keep it for now since we might have non confirming implementation - ASPerformBlockOnMainThread { - MainActor.assumeIsolated { - - 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 } } @@ -85,21 +110,15 @@ public final class ShapeLayerNode : ASDisplayNode, @preconcurrency ShapeDisplay return backingNode.layer.fillColor.map { UIColor(cgColor: $0) } } set { - // Keep it for now since we might have non confirming implementation - ASPerformBlockOnMainThread { - MainActor.assumeIsolated { - - 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() @@ -125,27 +144,16 @@ public final class ShapeLayerNode : ASDisplayNode, @preconcurrency ShapeDisplay } } - 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 dc95676..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,7 +200,7 @@ public class StyledEdgeNode: NamedDisplayNodeBase { updateBorder() updateStrategy() - self.borderNode.shapeLayer.fillRule = .evenOdd + self.borderNode.unsafeShapeLayer.fillRule = .evenOdd } private func updateBorder() { diff --git a/Sources/Components/Elements/GradientLayerNode.swift b/Sources/Components/Elements/GradientLayerNode.swift index f2f8e60..3c1ddc2 100644 --- a/Sources/Components/Elements/GradientLayerNode.swift +++ b/Sources/Components/Elements/GradientLayerNode.swift @@ -57,6 +57,7 @@ open class GradientLayerNode : ASDisplayNode, @unchecked Sendable { backgroundColor = .clear } + @MainActor open override func didLoad() { super.didLoad() @@ -66,7 +67,7 @@ open class GradientLayerNode : ASDisplayNode, @unchecked Sendable { } - /// why are we using both assert main thread and lock and dispatch ? + @MainActor open func setDescriptor(descriptor: LinearGradientDescriptor) { lock() diff --git a/Sources/Components/Tools/NamedDisplayNodeBase.swift b/Sources/Components/Tools/NamedDisplayNodeBase.swift index 7755fcc..46da24b 100644 --- a/Sources/Components/Tools/NamedDisplayNodeBase.swift +++ b/Sources/Components/Tools/NamedDisplayNodeBase.swift @@ -71,7 +71,6 @@ open class NamedDisplayNodeBase: ASDisplayNode, @unchecked Sendable { return self } - @preconcurrency @MainActor private func propagate(action: DisplayNodeAction) { for handler in __actionHandlers { From 0aff65c3d8166d42cc6da41b7675c95139724914 Mon Sep 17 00:00:00 2001 From: Antoine Marandon Date: Fri, 29 Nov 2024 12:08:49 +0900 Subject: [PATCH 5/8] Cleanup --- Package.swift | 2 +- Sources/Components/Compositions/OnAppearNode.swift | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Package.swift b/Package.swift index 093ce60..bb386bb 100644 --- a/Package.swift +++ b/Package.swift @@ -4,7 +4,7 @@ import PackageDescription let package = Package( name: "TextureSwiftSupport", platforms: [ - .iOS(.v15), + .iOS(.v13), ], products: [ .library(name: "TextureSwiftSupport", targets: ["TextureSwiftSupport"]), diff --git a/Sources/Components/Compositions/OnAppearNode.swift b/Sources/Components/Compositions/OnAppearNode.swift index 45a153f..8734774 100644 --- a/Sources/Components/Compositions/OnAppearNode.swift +++ b/Sources/Components/Compositions/OnAppearNode.swift @@ -74,15 +74,10 @@ public final class OnAppearNode: NamedDisplayCellNodeBas state = .init() bottomRightTiledLayerNode = .init(wrappedView: { - MainActor.assumeIsolated { - .init() - } + .init() }) topLeftTiledLayerNode = .init(wrappedView: { - MainActor.assumeIsolated { - // :/ https://github.com/swiftlang/swift/issues/76099 - .init() - } + .init() }) setNeedsLayout() From fe133f8bd105f1f9713e2dc260dcddf5c7b6fff5 Mon Sep 17 00:00:00 2001 From: Antoine Marandon Date: Fri, 29 Nov 2024 12:35:36 +0900 Subject: [PATCH 6/8] Fix typo --- Sources/Components/Compositions/ShapeLayerNode.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Components/Compositions/ShapeLayerNode.swift b/Sources/Components/Compositions/ShapeLayerNode.swift index fddcf42..946e7f0 100644 --- a/Sources/Components/Compositions/ShapeLayerNode.swift +++ b/Sources/Components/Compositions/ShapeLayerNode.swift @@ -72,7 +72,7 @@ public final class ShapeLayerNode : ASDisplayNode, MainActorShapeDisplaying, @un } @MainActor - /// Beware, direct access to lineWidth is not supported here when using usesInnerBorder, otherwise acess should be safe + /// Beware, direct access to lineWidth is not supported here when using usesInnerBorder, otherwise access should be safe public var unsafeShapeLayer: CAShapeLayer { backingNode.layer } From e150bb5c36349b80e6cf639150758188db5197d1 Mon Sep 17 00:00:00 2001 From: Antoine Marandon Date: Fri, 29 Nov 2024 16:04:48 +0900 Subject: [PATCH 7/8] Update: fix stackscrollnode --- Package.resolved | 7 ++- .../Containers/StackScrollNode.swift | 52 ++++++++++++------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/Package.resolved b/Package.resolved index b288bd2..4a4383d 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "1ec615bb9859848353ee388118e25fe6e7cc4baa377b5045fc273ea94edff5fc", + "originHash" : "d9e897732f60af4412110707787651b74e68d6f3e1b560c546e4b10fc8f816e7", "pins" : [ { "identity" : "descriptors", @@ -7,7 +7,7 @@ "location" : "https://github.com/ntnmrndn/Descriptors", "state" : { "branch" : "antoine/swift6", - "revision" : "b1eb25aa85dbcac3149438c7ac8d13949317ba45" + "revision" : "8f0c6b119cb15e09155460576773f1151c96ce62" } }, { @@ -16,10 +16,9 @@ "location" : "https://github.com/ntnmrndn/Texture.git", "state" : { "branch" : "antoine/swift6", - "revision" : "5bd706009aeee6711656cc993fdb60131c4d04f0" + "revision" : "2c945a0641d39628f06d21ef7b7f4fb64119812f" } } ], "version" : 3 } - diff --git a/Sources/Components/Containers/StackScrollNode.swift b/Sources/Components/Containers/StackScrollNode.swift index a98d2be..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, @unchecked Sendable { +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, @@ -63,6 +71,7 @@ open class StackScrollNode : NamedDisplayNodeBase, ASCollectionDelegate, ASColle return .init(layout: layout) } + @MainActor open func append(nodes: [ASCellNode]) { self.nodes += nodes @@ -73,6 +82,7 @@ open class StackScrollNode : NamedDisplayNodeBase, ASCollectionDelegate, ASColle } } + @MainActor open func removeAll() { self.nodes = [] @@ -82,6 +92,7 @@ open class StackScrollNode : NamedDisplayNodeBase, ASCollectionDelegate, ASColle } } + @MainActor open func replaceAll(nodes: [ASCellNode]) { self.nodes = nodes @@ -117,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( @@ -140,29 +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 } - nonisolated public func collectionNode(_ collectionNode: ASCollectionNode, numberOfItemsInSection section: Int) -> Int { - // I'm not sure why self is considered @mainactor here, ... - MainActor.assumeIsolated { - nodes.count - } - } - nonisolated public func collectionNode(_ collectionNode: ASCollectionNode, nodeForItemAt indexPath: IndexPath) -> ASCellNode { - /// See abobe - MainActor.assumeIsolated { - nodes[indexPath.item] - } + @objc public func collectionNode(_ collectionNode: ASCollectionNode, numberOfItemsInSection section: Int) -> Int { + nodes.count } - // MARK: - UIScrollViewDelegate - public func scrollViewDidScroll(_ scrollView: UIScrollView) { - onScrollViewDidScroll(scrollView) + @objc public func collectionNode(_ collectionNode: ASCollectionNode, nodeForItemAt indexPath: IndexPath) -> ASCellNode { + nodes[indexPath.item] } } - From f54bd9d0550f5fde6432d17367538042367117b4 Mon Sep 17 00:00:00 2001 From: Antoine Marandon Date: Fri, 29 Nov 2024 16:28:45 +0900 Subject: [PATCH 8/8] Improve code --- Sources/Components/Compositions/ViewNode.swift | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Sources/Components/Compositions/ViewNode.swift b/Sources/Components/Compositions/ViewNode.swift index b5a09fe..815fe8d 100644 --- a/Sources/Components/Compositions/ViewNode.swift +++ b/Sources/Components/Compositions/ViewNode.swift @@ -24,7 +24,7 @@ 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 { @@ -37,10 +37,7 @@ open class ViewNode: NamedDisplayNodeBase { ) { super.init() setViewBlock { - // https://github.com/swiftlang/swift/issues/76099 - MainActor.assumeIsolated { - wrappedView() - } + wrappedView() } } }