Skip to content

Instantly share code, notes, and snippets.

@woodycatliu
Created July 18, 2021 13:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save woodycatliu/82adb528e293d6af2aa4cfbe6f345b4e to your computer and use it in GitHub Desktop.
Save woodycatliu/82adb528e293d6af2aa4cfbe6f345b4e to your computer and use it in GitHub Desktop.
這是從 https://github.com/ailabstw/social-distancing-ios 搬運出來的 Observed 屬性包裝器,
import Foundation
@propertyWrapper
public class Observed<T> {
private var _value: T {
didSet {
execute()
}
}
private var _closure: ((T) -> Void)? {
didSet {
execute()
}
}
private var _queue: DispatchQueue? = nil
public var wrappedValue: T {
get {
_value
}
set {
_value = newValue
}
}
public var projectedValue: Observed<T> {
self
}
public init(wrappedValue: T, queue: DispatchQueue? = nil) {
self._value = wrappedValue
self._queue = queue
}
public func observe(using block: @escaping (T) -> Void) {
_closure = block
}
/**
callAsFunction 為 Swift 5.0 新的語法糖
請參考 [AppCoda](https://www.appcoda.com.tw/swift-5-2/)
*/
public func callAsFunction(using block: @escaping (T) -> Void) {
observe(using: block)
}
public func cancel() {
_closure = nil
}
private func execute() {
guard let closure = _closure else {
return
}
let value = _value
// If target queue is .main and currently running on main thread, no need to execute async.
// | Queue | Thread | Operation |
// | -------- | -------- | --------- |
// | nil | main | sync |
// | nil | not main | sync |
// | main | main | sync |
// | main | not main | async |
// | not main | main | async |
// | not main | not main | async |
if let queue = _queue, (queue != DispatchQueue.main || Thread.isMainThread == false) {
queue.async {
closure(value)
}
} else {
closure(value)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment