Skip to content

Instantly share code, notes, and snippets.

@Moximillian
Created May 25, 2018 12:01
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 Moximillian/6ec7e2d2e5b12c1bce0d09b7faa51ee2 to your computer and use it in GitHub Desktop.
Save Moximillian/6ec7e2d2e5b12c1bce0d09b7faa51ee2 to your computer and use it in GitHub Desktop.
import Foundation
extension NSObjectProtocol where Self: NSObject {
internal func store(associatedObject: Any) {
// store the container so that it can be called later, we do not need to explicitly retrieve it.
var associatedObjectStore = objc_getAssociatedObject(self, Unmanaged.passUnretained(self).toOpaque()) as? [Any] ?? []
associatedObjectStore.append(associatedObject)
objc_setAssociatedObject(self, Unmanaged.passUnretained(self).toOpaque(), associatedObjectStore, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
class Disposable {
typealias ClosureType = () -> Void
let closure: ClosureType
init(_ closure: @escaping ClosureType) {
self.closure = closure
}
deinit {
print("Disposable.\(#function)")
closure()
}
}
extension NSObjectProtocol where Self: NSObject {
func observe<Value>(_ keyPath: KeyPath<Self, Value>, onChange: @escaping (Value) -> ()) -> Disposable {
let observation = observe(keyPath, options: [.initial, .new]) { _, change in
// The guard is because of https://bugs.swift.org/browse/SR-6066
guard let newValue = change.newValue else { return }
onChange(newValue)
}
return Disposable { observation.invalidate() }
}
public func bind<Value, Target: NSObject>(_ sourceKeyPath: KeyPath<Self, Value>, to target: Target, at targetKeyPath: ReferenceWritableKeyPath<Target, Value>) {
let disposable = observe(sourceKeyPath) { [weak target] in
target?[keyPath: targetKeyPath] = $0 }
target.store(associatedObject: disposable)
}
}
final class Foo: NSObject {
@objc dynamic var bar: Int = 0 {
didSet {
print("Bar changed to: \(bar)")
}
}
}
final class Zoq: NSObject {
var fot: Int = 0 {
didSet {
print("fot changed to: \(fot)")
}
}
deinit {
print("Zoq.\(#function)")
}
}
let foo = Foo()
var zoq = Zoq()
foo.bar = 42
foo.bind(\.bar, to: zoq, at: \.fot)
foo.bar = 2
zoq = Zoq() // Zoq.deinit, Disposable.deinit
foo.bar = 4
zoq.fot
foo.bar = 8
zoq.fot
foo.bar = 12
zoq.fot
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment