Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Sources/Control/AsyncButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@ import SwiftUI
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *)
public struct AsyncButton<Label: View>: AsyncControl {
public typealias Base = Button<Label>
package typealias Value = ()

private let priority: TaskPriority
private let action: @Sendable () async -> Void
private let base: (_ trigger: _AsyncControlTrigger?) -> Base
private let base: (_ trigger: _AsyncControlTrigger<Value>?) -> Base

@State private var trigger: _AsyncControlTrigger? = nil
@State private var trigger: _AsyncControlTrigger<Value>? = nil

private init(
priority: TaskPriority,
@_inheritActorContext action: @escaping @Sendable () async -> Void,
base: @escaping (_ trigger: _AsyncControlTrigger?) -> Base
base: @escaping (_ trigger: _AsyncControlTrigger<Value>?) -> Base
) {
self.priority = priority
self.action = action
Expand Down
1 change: 1 addition & 0 deletions Sources/Core/AsyncControl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ import SwiftUI
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *)
package protocol AsyncControl: View {
associatedtype Base: View
associatedtype Value
}
33 changes: 20 additions & 13 deletions Sources/Core/AsyncControlView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@
import SwiftUI

@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *)
package struct AsyncControlView<Base: View>: View {
package struct AsyncControlView<Base: View, Value>: View {
private let priority: TaskPriority
private let action: @Sendable () async -> Void
private let action: @Sendable (_ value: Value) async -> Void
private let base: Base

@State private var idValue: Bool? = nil
@State private var idValue: IDValue? = nil

@inlinable
package init(
priority: TaskPriority = .userInitiated,
@_inheritActorContext action: @escaping @Sendable () async -> Void,
@_inheritActorContext action: @escaping @Sendable (_ value: Value) async -> Void,
base: Base
) {
self.action = action
Expand All @@ -28,21 +28,28 @@ package struct AsyncControlView<Base: View>: View {

package var body: some View {
base
.preference(key: _AsyncControlTriggerPreferenceKey.self, value: .init(action: trigger))
.preference(key: _AsyncControlTriggerPreferenceKey.self, value: .init(action: trigger(newValue:)))
.task(id: idValue, priority: priority, performAction)
}

private func trigger() {
if idValue == nil {
idValue = true
} else {
idValue?.toggle()
}
private func trigger(newValue: Value) {
idValue = .init(value: newValue)
}

@Sendable
private func performAction() async {
guard idValue != nil else { return }
await action()
guard let value = idValue?.value else { return }
await action(value)
}
}

extension AsyncControlView {
private struct IDValue: Identifiable, Equatable {
fileprivate let id = UUID()
fileprivate let value: Value

fileprivate static func == (lhs: Self, rhs: Self) -> Bool {
lhs.id == rhs.id
}
}
}
14 changes: 9 additions & 5 deletions Sources/Core/_AsyncControlTrigger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ import Foundation

/// - Warning: This `Equatable` compliance is a dummy implementation added for use in `SwiftUI.View.onPreferenceChange(_:perform:)`.
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *)
package struct _AsyncControlTrigger: Equatable {
private let action: () -> Void
package struct _AsyncControlTrigger<Value>: Equatable {
private let action: (_ value: Value) -> Void

init(action: @escaping () -> Void) {
init(action: @escaping (_ value: Value) -> Void) {
self.action = action
}

package func callAsFunction() {
action()
package func callAsFunction(_ value: Value) {
action(value)
}

package func callAsFunction() where Value == Void {
callAsFunction(())
}

package static func == (lhs: Self, rhs: Self) -> Bool {
Expand Down
6 changes: 3 additions & 3 deletions Sources/Core/_AsyncControlTriggerPreferenceKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import SwiftUI

@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *)
package struct _AsyncControlTriggerPreferenceKey: PreferenceKey {
package static let defaultValue = _AsyncControlTrigger(action: {})
package static func reduce(value: inout _AsyncControlTrigger, nextValue: () -> _AsyncControlTrigger) {}
package struct _AsyncControlTriggerPreferenceKey<Value>: PreferenceKey {
package static var defaultValue: _AsyncControlTrigger<Value> { fatalError("This property is not expected to be called.") }
package static func reduce(value: inout _AsyncControlTrigger<Value>, nextValue: () -> _AsyncControlTrigger<Value>) { fatalError("This function is not expected to be called.") }
}