-
-
Save ilyapuchka/8dd3148d73c533b17d0968db18a67254 to your computer and use it in GitHub Desktop.
References Blogpost
This file contains 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
//: Playground - noun: a place where people can play | |
import Foundation | |
final class Disposable { | |
private let dispose: () -> () | |
init(_ dispose: @escaping () -> ()) { | |
self.dispose = dispose | |
} | |
deinit { | |
dispose() | |
} | |
} | |
final class Ref<A> { | |
typealias Observer = (A) -> () | |
fileprivate let _get: () -> A | |
fileprivate let _set: (A) -> () | |
private let _addObserver: (@escaping Observer) -> Disposable | |
var value: A { | |
get { | |
return _get() | |
} | |
set { | |
_set(newValue) | |
} | |
} | |
init(get: @escaping () -> A, set: @escaping (A) -> (), addObserver: @escaping (@escaping Observer) -> Disposable) { | |
_get = get | |
_set = set | |
_addObserver = addObserver | |
} | |
func addObserver(observer: @escaping Observer) -> Disposable { | |
return _addObserver(observer) | |
} | |
} | |
extension Ref { | |
convenience init(initialValue: A) { | |
var observers: [Int: Observer] = [:] | |
var theValue = initialValue { | |
didSet { | |
observers.values.forEach { $0(theValue) } | |
} | |
} | |
var freshId = (Int.min...).makeIterator() | |
let get = { theValue } | |
let set = { newValue in | |
theValue = newValue | |
} | |
let addObserver = { (newObserver: @escaping Observer) -> Disposable in | |
let id = freshId.next()! | |
observers[id] = newObserver | |
return Disposable { | |
observers[id] = nil | |
} | |
} | |
self.init(get: get, set: set, addObserver: addObserver) | |
} | |
} | |
extension Ref { | |
subscript<B>(keyPath: WritableKeyPath<A,B>) -> Ref<B> { | |
let parent = self | |
return Ref<B>(get: { parent._get()[keyPath: keyPath] }, set: { | |
var oldValue = parent.value | |
oldValue[keyPath: keyPath] = $0 | |
parent._set(oldValue) | |
}, addObserver: { observer in | |
parent.addObserver { observer($0[keyPath: keyPath]) } | |
}) | |
} | |
} | |
extension Ref where A: MutableCollection { | |
subscript(index: A.Index) -> Ref<A.Element> { | |
return Ref<A.Element>(get: { self._get()[index] }, set: { newValue in | |
var old = self.value | |
old[index] = newValue | |
self._set(old) | |
}, addObserver: { observer in | |
self.addObserver { observer($0[index]) } | |
}) | |
} | |
} | |
struct History<A> { | |
private let initialValue: A | |
private var history: [A] = [] | |
private var redoStack: [A] = [] | |
var value: A { | |
get { | |
return history.last ?? initialValue | |
} | |
set { | |
history.append(newValue) | |
redoStack = [] | |
} | |
} | |
init(initialValue: A) { | |
self.initialValue = initialValue | |
} | |
mutating func undo() { | |
guard let item = history.popLast() else { return } | |
redoStack.append(item) | |
} | |
mutating func redo() { | |
guard let item = redoStack.popLast() else { return } | |
history.append(item) | |
} | |
} | |
struct Address { | |
var street: String | |
} | |
struct Person { | |
var name: String | |
var addresses: [Address] | |
} | |
typealias Addressbook = [Person] | |
let source: Ref<History<Addressbook>> = Ref(initialValue: History(initialValue: [])) | |
let addressBook: Ref<Addressbook> = source[\.value] | |
addressBook.value.append(Person(name: "Test", addresses: [])) | |
addressBook[0].value.name = "New Name" | |
print(addressBook[0].value) | |
source.value.undo() | |
print(addressBook[0].value) | |
source.value.redo() | |
var twoPeople: Ref<Addressbook> = Ref(initialValue: | |
[Person(name: "One", addresses: []), | |
Person(name: "Two", addresses: [])]) | |
let p0 = twoPeople[0] | |
twoPeople.value.removeFirst() | |
print(p0.value) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment