Skip to content

Instantly share code, notes, and snippets.

@chriseidhof
Created June 13, 2019 07:36
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chriseidhof/9652a0d7144f8f1805ef72bdcffd0d88 to your computer and use it in GitHub Desktop.
Save chriseidhof/9652a0d7144f8f1805ef72bdcffd0d88 to your computer and use it in GitHub Desktop.
/// Similar to a `Binding`, but this is also observable/dynamic.
@propertyDelegate
@dynamicMemberLookup
final class Derived<A>: BindableObject {
let didChange = PassthroughSubject<A, Never>()
fileprivate var cancellables: [AnyCancellable] = []
private let get: () -> (A)
private let mutate: ((inout A) -> ()) -> ()
init(get: @escaping () -> A, mutate: @escaping ((inout A) -> ()) -> ()) {
self.get = get
self.mutate = mutate
}
var value: A {
get { get() }
set { mutate { $0 = newValue } }
}
subscript<U: Equatable>(dynamicMember keyPath: WritableKeyPath<A, U>) -> Derived<U> {
let result = Derived<U>(get: {
let value = self.get()[keyPath: keyPath]
return value
}, mutate: { f in
self.mutate { (a: inout A) in
f(&a[keyPath: keyPath])
}
})
let c = didChange.map { $0[keyPath: keyPath] }.removeDuplicates().sink { value in
result.didChange.send(value)
}
cancellables.append(AnyCancellable(c))
return result
}
var binding: Binding<A> {
return Binding<A>(getValue: { self.value }, setValue: { self.value = $0 })
}
deinit {
for c in cancellables {
c.cancel()
}
}
}
final class SimpleStore<A>: BindableObject {
let didChange =
PassthroughSubject<A, Never>()
init(_ value: A) { self.value = value }
var value: A {
didSet {
didChange.send(value)
}
}
var bindable: Derived<A> {
let result = Derived<A>(get: {
self.value
}, mutate: { f in
f(&self.value)
})
let c = self.didChange.sink { [weak result] value in
result?.didChange.send(value)
}
result.cancellables.append(AnyCancellable(c))
return result
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment