Skip to content
This repository was archived by the owner on May 26, 2020. It is now read-only.

Commit c07455c

Browse files
dmcrodriguesandersio
authored andcommitted
Propagate changes using snapshots plus the indices affected by the changes
1 parent 1461e5f commit c07455c

File tree

5 files changed

+411
-422
lines changed

5 files changed

+411
-422
lines changed

ReactiveCollections.xcodeproj/project.pbxproj

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@
4444
7DE06DDF1DFADD9B003303AB /* ReactiveCollections.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7DE06DBD1DFADCAE003303AB /* ReactiveCollections.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
4545
7DE06DE01DFADDA0003303AB /* ReactiveSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7DE06DD41DFADCE4003303AB /* ReactiveSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
4646
7DE06DE11DFADDA0003303AB /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7DE06DD51DFADCE4003303AB /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
47-
7DF60EED1E007DEF0096283B /* Changeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DF60EEC1E007DEF0096283B /* Changeset.swift */; };
48-
7DF60EEE1E007DEF0096283B /* Changeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DF60EEC1E007DEF0096283B /* Changeset.swift */; };
49-
7DF60EEF1E007DEF0096283B /* Changeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DF60EEC1E007DEF0096283B /* Changeset.swift */; };
50-
7DF60EF01E007DEF0096283B /* Changeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DF60EEC1E007DEF0096283B /* Changeset.swift */; };
47+
7DF60EED1E007DEF0096283B /* Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DF60EEC1E007DEF0096283B /* Delta.swift */; };
48+
7DF60EEE1E007DEF0096283B /* Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DF60EEC1E007DEF0096283B /* Delta.swift */; };
49+
7DF60EEF1E007DEF0096283B /* Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DF60EEC1E007DEF0096283B /* Delta.swift */; };
50+
7DF60EF01E007DEF0096283B /* Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DF60EEC1E007DEF0096283B /* Delta.swift */; };
5151
/* End PBXBuildFile section */
5252

5353
/* Begin PBXContainerItemProxy section */
@@ -134,7 +134,7 @@
134134
7DE06DC51DFADCAF003303AB /* ReactiveCollectionsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCollectionsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
135135
7DE06DD41DFADCE4003303AB /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = Carthage/Build/tvOS/ReactiveSwift.framework; sourceTree = "<group>"; };
136136
7DE06DD51DFADCE4003303AB /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/tvOS/Result.framework; sourceTree = "<group>"; };
137-
7DF60EEC1E007DEF0096283B /* Changeset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Changeset.swift; sourceTree = "<group>"; };
137+
7DF60EEC1E007DEF0096283B /* Delta.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Delta.swift; sourceTree = "<group>"; };
138138
/* End PBXFileReference section */
139139

140140
/* Begin PBXFrameworksBuildPhase section */
@@ -252,7 +252,7 @@
252252
children = (
253253
7D69AAE01DF9CEE500FCB568 /* ReactiveCollections.h */,
254254
7D69AAF71DF9D07800FCB568 /* ReactiveArray.swift */,
255-
7DF60EEC1E007DEF0096283B /* Changeset.swift */,
255+
7DF60EEC1E007DEF0096283B /* Delta.swift */,
256256
7D3D8BE41DF9EAE000E90921 /* Supporting Files */,
257257
);
258258
name = ReactiveCollections;
@@ -606,7 +606,7 @@
606606
buildActionMask = 2147483647;
607607
files = (
608608
7D69AAF81DF9D07800FCB568 /* ReactiveArray.swift in Sources */,
609-
7DF60EED1E007DEF0096283B /* Changeset.swift in Sources */,
609+
7DF60EED1E007DEF0096283B /* Delta.swift in Sources */,
610610
);
611611
runOnlyForDeploymentPostprocessing = 0;
612612
};
@@ -623,7 +623,7 @@
623623
buildActionMask = 2147483647;
624624
files = (
625625
7DC1E2DA1DFADF9B00A61745 /* ReactiveArray.swift in Sources */,
626-
7DF60EF01E007DEF0096283B /* Changeset.swift in Sources */,
626+
7DF60EF01E007DEF0096283B /* Delta.swift in Sources */,
627627
);
628628
runOnlyForDeploymentPostprocessing = 0;
629629
};
@@ -632,7 +632,7 @@
632632
buildActionMask = 2147483647;
633633
files = (
634634
7DE06DAB1DFADB0F003303AB /* ReactiveArray.swift in Sources */,
635-
7DF60EEE1E007DEF0096283B /* Changeset.swift in Sources */,
635+
7DF60EEE1E007DEF0096283B /* Delta.swift in Sources */,
636636
);
637637
runOnlyForDeploymentPostprocessing = 0;
638638
};
@@ -649,7 +649,7 @@
649649
buildActionMask = 2147483647;
650650
files = (
651651
7DE06DDA1DFADD69003303AB /* ReactiveArray.swift in Sources */,
652-
7DF60EEF1E007DEF0096283B /* Changeset.swift in Sources */,
652+
7DF60EEF1E007DEF0096283B /* Delta.swift in Sources */,
653653
);
654654
runOnlyForDeploymentPostprocessing = 0;
655655
};

Sources/Changeset.swift

Lines changed: 0 additions & 121 deletions
This file was deleted.

Sources/Delta.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import Foundation
2+
3+
public extension IndexSet {
4+
5+
public static var empty: IndexSet {
6+
return IndexSet()
7+
}
8+
}
9+
10+
public struct Delta<Snapshot: Collection, ChangeRepresentation> {
11+
public let previous: Snapshot
12+
public let current: Snapshot
13+
14+
public let inserts: ChangeRepresentation
15+
public let deletes: ChangeRepresentation
16+
public let updates: ChangeRepresentation
17+
}
18+
19+
extension Delta where Snapshot.Iterator.Element: Equatable, ChangeRepresentation: Equatable {
20+
21+
public static func ==(lhs: Delta<Snapshot, ChangeRepresentation>, rhs: Delta<Snapshot, ChangeRepresentation>) -> Bool {
22+
23+
guard lhs.inserts == rhs.inserts
24+
&& lhs.deletes == rhs.deletes
25+
else { return false }
26+
27+
return lhs.previous == rhs.previous && lhs.current == rhs.current
28+
}
29+
}
30+
31+
fileprivate extension Collection where Iterator.Element: Equatable {
32+
33+
fileprivate static func ==(lhs: Self, rhs: Self) -> Bool {
34+
35+
guard lhs.count == rhs.count && lhs.count == rhs.count
36+
else { return false }
37+
38+
guard zip(lhs, rhs).map(==).reduce(true, { $0 && $1 })
39+
else { return false }
40+
41+
return true
42+
}
43+
}

Sources/ReactiveArray.swift

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,19 @@ import Result
44

55
public final class ReactiveArray<Element> {
66

7-
fileprivate var elements: ContiguousArray<Element>
8-
9-
fileprivate let innerObserver: Observer<Changeset<Element>, NoError>
7+
public typealias Snapshot = ContiguousArray<Element>
8+
public typealias Change = Delta<Snapshot, IndexSet>
109

11-
public let signal: Signal<Changeset<Element>, NoError>
12-
13-
public var producer: SignalProducer<Changeset<Element>, NoError> {
14-
return SignalProducer.attempt { [weak self] () -> Result<Changeset<Element>, NSError> in
15-
guard let `self` = self else { return .failure(NSError()) }
10+
public let signal: Signal<Change, NoError>
1611

17-
return .success(
18-
Changeset.generate(
19-
insert: (items: self[self.indices], range: Range(self.indices)),
20-
remove: nil
21-
)
22-
)
12+
fileprivate var elements: ContiguousArray<Element>
2313

24-
}
25-
.flatMapError { _ in .empty }
26-
.concat(SignalProducer(signal: signal))
27-
}
14+
fileprivate let innerObserver: Observer<Change, NoError>
2815

2916
public init(_ elements: [Element]) {
3017
self.elements = ContiguousArray(elements)
3118

32-
(signal, innerObserver) = Signal<Changeset<Element>, NoError>.pipe()
19+
(signal, innerObserver) = Signal<Change, NoError>.pipe()
3320
}
3421

3522
public convenience init() {
@@ -42,6 +29,26 @@ public final class ReactiveArray<Element> {
4229

4330
}
4431

32+
extension ReactiveArray {
33+
34+
public var producer: SignalProducer<Change, NoError> {
35+
return SignalProducer<Change, NSError>.attempt { [weak self] in
36+
guard let `self` = self else { return .failure(NSError()) }
37+
38+
return .success(
39+
Delta(
40+
previous: [],
41+
current: self.elements,
42+
inserts: IndexSet(integersIn: self.indices),
43+
deletes: .empty,
44+
updates: .empty
45+
)
46+
)}
47+
.flatMapError { _ in .empty }
48+
.concat(SignalProducer(signal: signal))
49+
}
50+
}
51+
4552
// MARK: - ExpressibleByArrayLiteral
4653

4754
extension ReactiveArray: ExpressibleByArrayLiteral {
@@ -125,14 +132,20 @@ extension ReactiveArray: RangeReplaceableCollection {
125132
if keepCapacity {
126133
removeSubrange(indices)
127134
} else {
128-
let changeset = Changeset.generate(
129-
insert: nil,
130-
remove: (items: self[indices], range: Range(indices))
131-
)
135+
136+
let previous = elements
132137

133138
elements.removeAll()
134139

135-
innerObserver.send(value: changeset)
140+
innerObserver.send(value:
141+
Delta(
142+
previous: previous,
143+
current: elements,
144+
inserts: .empty,
145+
deletes: IndexSet(integersIn: previous.indices),
146+
updates: .empty
147+
)
148+
)
136149
}
137150
}
138151

@@ -188,14 +201,23 @@ extension ReactiveArray: RangeReplaceableCollection {
188201

189202
public func replaceSubrange<C>(_ subrange: Range<Int>, with newElements: C) where C: Collection, C.Iterator.Element == Element {
190203

191-
let changeset = Changeset.generate(
192-
insert: (items: ArraySlice(newElements), range: subrange),
193-
remove: (items: elements[subrange], range: subrange)
194-
)
204+
let previous = elements
195205

196206
elements.replaceSubrange(subrange, with: newElements)
197207

198-
innerObserver.send(value: changeset)
208+
let inserts = IndexSet(integersIn: subrange.lowerBound..<subrange.lowerBound.advanced(by: newElements.underestimatedCount))
209+
let deletes = IndexSet(integersIn: subrange)
210+
let updates = inserts.intersection(deletes)
211+
212+
innerObserver.send(value:
213+
Delta(
214+
previous: previous,
215+
current: elements,
216+
inserts: inserts.subtracting(updates),
217+
deletes: deletes.subtracting(updates),
218+
updates: updates
219+
)
220+
)
199221
}
200222

201223
public func reserveCapacity(_ n: Int) {

0 commit comments

Comments
 (0)