I have the following UndoableArray class which acts like a normal Array but also has undo support:
public final class UndoableArray<Element>: RandomAccessCollection, RangeReplaceableCollection {
public typealias Index = Array<Element>.Index
private(set) var elements: [Element]
public init() {
self.elements = []
}
public init<S>(_ elements: S) where S: Sequence, Element == S.Element {
self.elements = Array(elements)
}
public var startIndex: Index {
return elements.startIndex
}
public var endIndex: Index {
return elements.endIndex
}
public subscript(position: Index) -> Element {
get {
return elements[position]
}
set {
let oldValue = elements[position]
elements[position] = newValue
recordUndo { [self] in
self[position] = oldValue
}
}
}
public func replaceSubrange<C>(_ subrange: Range<Array<Element>.Index>, with newElements: C) where C: Collection, Element == C.Element {
let oldElements = Array(elements[subrange])
elements.replaceSubrange(subrange, with: newElements)
let subrange = subrange.lowerBound..<(subrange.lowerBound + newElements.count)
recordUndo { [self] in
replaceSubrange(subrange, with: oldElements)
}
}
}
func recordUndo(_ block: () -> Void) {}
The problem is that RangeReplaceableCollection seems to assume that the implementing type is a struct, while I have a class. For example the + operator is documented as
Creates a new collection by concatenating the elements of a collection and a sequence.
but the following code shows that the left collection is mutated instead of creating a new one:
let a = UndoableArray([0])
let b = UndoableArray([1])
let _ = a + b
print(a.elements, b.elements) // output: [0, 1] [1]
Some time ago I added the following code because apparently filter was acting weird, but at the moment I'm unable to reproduce any unexpected behaviour:
public func filter(_ isIncluded: (Element) throws -> Bool) rethrows -> [Element] {
return try elements.filter(isIncluded)
}
I would change final class to struct, but then I get compiler errors like
recordUndo { [self] in
self[position] = oldValue // error: Cannot assign through subscript: 'self' is an immutable capture
}
What would be the best way to solve these issues? I'm afraid that a custom implementation of + would not be enough, and one day I'll use another method of RangeReplaceableCollection without noticing soon enough that it behaves incorrectly.