Last active
January 24, 2019 18:35
-
-
Save Dev1an/94106db5caa37e824a7b7c49bf763c40 to your computer and use it in GitHub Desktop.
An observable CustomStringConvertible, RandomAccess-, RangeReplaceableCollection in 50 lines of swift
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
| // Created by Damiaan on 23/01/2019. | |
| // Copyright © 2019 Devian. All rights reserved. | |
| class SmartCollection<Element> { | |
| class Reference { | |
| private (set) var pointee: Element | |
| init(to element: Element) { | |
| pointee = element | |
| } | |
| fileprivate subscript<Value>(smartPath: WritableKeyPath<Element, Value>) -> Value { | |
| get { return pointee[keyPath: smartPath] } | |
| set { | |
| pointee[keyPath: smartPath] = newValue | |
| } | |
| } | |
| } | |
| private (set) var references = [Reference]() | |
| typealias FieldChangeSubscription = ((_ index: Int, _ changedKeyPath: PartialKeyPath<Element>) -> Void) | |
| typealias ReplaceSubscription = ((_ subrange: Range<Int>, _ newElements: Array<Element>) -> Void) | |
| var preFieldChangeSubscriptions = [FieldChangeSubscription]() | |
| var postFieldChangeSubscriptions = [FieldChangeSubscription]() | |
| var preReplaceSubscriptions = [ReplaceSubscription]() | |
| var postReplaceSubscriptions = [ReplaceSubscription]() | |
| required init() {} | |
| subscript<Value>(_ index: Int, keyPath: WritableKeyPath<Element, Value>) -> Value { | |
| get { return references[index][keyPath] } | |
| set { | |
| let notify: (FieldChangeSubscription)->() = { $0(index, keyPath) } | |
| preFieldChangeSubscriptions.forEach(notify) | |
| references[index][keyPath] = newValue | |
| postFieldChangeSubscriptions.forEach(notify) | |
| } | |
| } | |
| } | |
| extension SmartCollection: RandomAccessCollection { | |
| var startIndex: Int { return references.startIndex } | |
| var endIndex: Int { return references.endIndex} | |
| subscript(_ index: Int) -> Element { | |
| return references[index].pointee | |
| } | |
| } | |
| extension SmartCollection: RangeReplaceableCollection { | |
| func replaceSubrange<C, R>(_ subrange: R, with newElements: C) where C : Collection, R : RangeExpression, Element == C.Element, Int == R.Bound { | |
| let notify: (ReplaceSubscription)->() = { $0(subrange.relative(to: self.references), Array(newElements)) } | |
| preReplaceSubscriptions.forEach(notify) | |
| references.replaceSubrange(subrange, with: newElements.map(Reference.init)) | |
| postReplaceSubscriptions.forEach(notify) | |
| } | |
| } | |
| extension SmartCollection: CustomStringConvertible where Element: CustomStringConvertible { | |
| var description: String { | |
| return "SmartCollection " + references.map {$0.pointee.description}.description | |
| } | |
| } | |
| extension SmartCollection.Reference: CustomStringConvertible where Element: CustomStringConvertible { | |
| var description: String { | |
| return "Reference to \(pointee)" | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment