From da58b01cdb0c13d0921bbb64aa1d433add025c06 Mon Sep 17 00:00:00 2001 From: Muukii Date: Tue, 5 Mar 2024 03:32:44 +0900 Subject: [PATCH] Update --- Sources/Verge/Library/StoreSubscription.swift | 25 +++++++-- Sources/Verge/Store/Store.swift | 56 +++++++++++++++++-- 2 files changed, 71 insertions(+), 10 deletions(-) diff --git a/Sources/Verge/Library/StoreSubscription.swift b/Sources/Verge/Library/StoreSubscription.swift index 838d9f0fad..399d6a1b1f 100644 --- a/Sources/Verge/Library/StoreSubscription.swift +++ b/Sources/Verge/Library/StoreSubscription.swift @@ -103,6 +103,8 @@ public final class StoreStateSubscription: Hashable, Cancellable, @unchecked Sen private let wasCancelled = ManagedAtomic(false) + private let isSuspending: ManagedAtomic = .init(false) + private var entranceForSuspension: ManagedAtomic = .init(0) private var source: AtomicReferenceStorage @@ -129,7 +131,7 @@ public final class StoreStateSubscription: Hashable, Cancellable, @unchecked Sen return } - source.dispose().cancel() + AtomicReferenceStorage.atomicLoad(at: &source, ordering: .relaxed).cancel() associatedStore = nil } @@ -140,7 +142,7 @@ public final class StoreStateSubscription: Hashable, Cancellable, @unchecked Sen return } - source.dispose().cancel() + AtomicReferenceStorage.atomicLoad(at: &source, ordering: .relaxed).cancel() } @@ -164,9 +166,16 @@ public final class StoreStateSubscription: Hashable, Cancellable, @unchecked Sen return } + defer { + entranceForSuspension.wrappingDecrement(ordering: .sequentiallyConsistent) + } + + guard isSuspending.compareExchange(expected: false, desired: true, ordering: .sequentiallyConsistent).exchanged else { + return + } + onAction(self, .suspend) - entranceForSuspension.wrappingDecrement(ordering: .sequentiallyConsistent) } public func resume() { @@ -180,9 +189,15 @@ public final class StoreStateSubscription: Hashable, Cancellable, @unchecked Sen return } - onAction(self, .resume) + defer { + entranceForSuspension.wrappingDecrement(ordering: .sequentiallyConsistent) + } + + guard isSuspending.compareExchange(expected: true, desired: false, ordering: .sequentiallyConsistent).exchanged else { + return + } - entranceForSuspension.wrappingDecrement(ordering: .sequentiallyConsistent) + onAction(self, .resume) } func associate(store: some StoreType) -> StoreStateSubscription { diff --git a/Sources/Verge/Store/Store.swift b/Sources/Verge/Store/Store.swift index 1893714987..0b0a27bd80 100644 --- a/Sources/Verge/Store/Store.swift +++ b/Sources/Verge/Store/Store.swift @@ -895,24 +895,70 @@ private final class StoreSubscriptionView: UIView { let store: Store = .init(initialState: .init()) private let label = UILabel() + private var subscription: StoreStateSubscription? override init(frame: CGRect) { super.init(frame: frame) - backgroundColor = .red + backgroundColor = .systemBackground - let button = UIButton.init(configuration: .bordered()) - button.addAction(.init(handler: { action in + let upButton = UIButton.init(configuration: .bordered()) + upButton.setTitle("Up", for: .normal) + upButton.addAction(.init(handler: { [weak self] action in + + self?.store.commit { + $0.count += 1 + } + + }), for: .touchUpInside) + + let suspendButton = UIButton.init(configuration: .bordered()) + suspendButton.setTitle("Suspend", for: .normal) + suspendButton.addAction(.init(handler: { [weak self] action in + + self?.subscription?.suspend() + + }), for: .touchUpInside) + + let resumeButton = UIButton.init(configuration: .bordered()) + resumeButton.setTitle("Resume", for: .normal) + resumeButton.addAction(.init(handler: { [weak self] action in + + self?.subscription?.resume() }), for: .touchUpInside) + let stack = UIStackView() stack.addArrangedSubview(label) - stack.addArrangedSubview(button) + stack.addArrangedSubview(upButton) + stack.addArrangedSubview(suspendButton) + stack.addArrangedSubview(resumeButton) + + stack.axis = .vertical + stack.distribution = .equalCentering addSubview(stack) - stack.autoresizingMask = + stack.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate( + [ + stack.topAnchor.constraint(equalTo: topAnchor), + stack.leadingAnchor.constraint(equalTo: leadingAnchor), + stack.trailingAnchor.constraint(equalTo: trailingAnchor), + stack.bottomAnchor.constraint(equalTo: bottomAnchor) + ] + ) + + subscription = store.sinkState { [weak self] state in + guard let self else { return } + + state.ifChanged(\.count).do { value in + self.label.text = value.description + } + + } + } required init?(coder: NSCoder) {