Last active
August 16, 2024 12:08
-
-
Save twittemb/47f8fa0950d44749a0d328afb8a2584c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension DispatchTimeInterval { | |
var nanoseconds: UInt64 { | |
switch self { | |
case .nanoseconds(let value) where value >= 0: return UInt64(value) | |
case .microseconds(let value) where value >= 0: return UInt64(value) * 1000 | |
case .milliseconds(let value) where value >= 0: return UInt64(value) * 1_000_000 | |
case .seconds(let value) where value >= 0: return UInt64(value) * 1_000_000_000 | |
case .never: return .zero | |
default: return .zero | |
} | |
} | |
} | |
final class UncheckedSendable<Value>: @unchecked Sendable { | |
var storage: Value | |
let lock = NSRecursiveLock() | |
init(_ value: Value) { | |
self.storage = value | |
} | |
var value: Value { | |
get { | |
defer { self.lock.unlock() } | |
self.lock.lock() | |
return self.storage | |
} | |
set { | |
defer { self.lock.unlock() } | |
self.lock.lock() | |
self.storage = newValue | |
} | |
} | |
} | |
#if canImport(SwiftUI) | |
import SwiftUI | |
extension Binding { | |
struct DueValue { | |
let value: Value | |
let dueTime: DispatchTime | |
} | |
public func debounce(for dueTime: DispatchTimeInterval) -> Self { | |
let lastKnownValue = UncheckedSendable<DueValue?>(nil) | |
let debouncing = UncheckedSendable(false) | |
return Binding { | |
self.wrappedValue | |
} set: { value in | |
if debouncing.value { | |
let newValue = DueValue(value: value, dueTime: DispatchTime.now().advanced(by: dueTime)) | |
lastKnownValue.value = newValue | |
} else { | |
debouncing.value = true | |
Task { | |
var timeToSleep = dueTime.nanoseconds | |
var currentValue = value | |
repeat { | |
lastKnownValue.value = nil | |
try? await Task.sleep(nanoseconds: timeToSleep) | |
if let lastKnownValue = lastKnownValue.value { | |
timeToSleep = DispatchTime.now().distance(to: lastKnownValue.dueTime).nanoseconds | |
currentValue = lastKnownValue.value | |
} | |
} while lastKnownValue.value != nil | |
debouncing.value = false | |
self.wrappedValue = currentValue | |
} | |
} | |
} | |
} | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment