Skip to content

Commit 93f3efe

Browse files
committed
♻️ ✨ Move Combine nibbles into Fuse
1 parent 50513c6 commit 93f3efe

File tree

11 files changed

+174
-52
lines changed

11 files changed

+174
-52
lines changed

.swiftpm/xcode/xcshareddata/xcschemes/swift-nibbles-Package.xcscheme

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,20 @@
118118
ReferencedContainer = "container:">
119119
</BuildableReference>
120120
</BuildActionEntry>
121+
<BuildActionEntry
122+
buildForTesting = "YES"
123+
buildForRunning = "YES"
124+
buildForProfiling = "YES"
125+
buildForArchiving = "YES"
126+
buildForAnalyzing = "YES">
127+
<BuildableReference
128+
BuildableIdentifier = "primary"
129+
BlueprintIdentifier = "Fuse"
130+
BuildableName = "Fuse"
131+
BlueprintName = "Fuse"
132+
ReferencedContainer = "container:">
133+
</BuildableReference>
134+
</BuildActionEntry>
121135
</BuildActionEntries>
122136
</BuildAction>
123137
<TestAction
@@ -182,6 +196,16 @@
182196
ReferencedContainer = "container:">
183197
</BuildableReference>
184198
</TestableReference>
199+
<TestableReference
200+
skipped = "NO">
201+
<BuildableReference
202+
BuildableIdentifier = "primary"
203+
BlueprintIdentifier = "FuseTests"
204+
BuildableName = "FuseTests"
205+
BlueprintName = "FuseTests"
206+
ReferencedContainer = "container:">
207+
</BuildableReference>
208+
</TestableReference>
185209
</Testables>
186210
</TestAction>
187211
<LaunchAction

Package.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,23 @@ let package = Package(
1111
.watchOS(.v9),
1212
],
1313
products: [
14-
.library(name: "BuffableAsyncPublishers", targets: ["BuffableAsyncPublishers"]),
1514
.library(name: "Cache", targets: ["Cache"]),
1615
.library(name: "Extensions", targets: ["Extensions"]),
16+
.library(name: "Fuse", targets: ["Fuse"]),
1717
.library(name: "HTTPNetworking", targets: ["HTTPNetworking"]),
1818
.library(name: "Identified", targets: ["Identified"]),
1919
.plugin(name: "Create TCA Feature", targets: ["Create TCA Feature"])
2020
],
2121
targets: [
22-
.target(name: "BuffableAsyncPublishers"),
23-
.testTarget(name: "BuffableAsyncPublishersTests", dependencies: ["BuffableAsyncPublishers"]),
24-
2522
.target(name: "Cache"),
2623
.testTarget(name: "CacheTests", dependencies: ["Cache"]),
2724

2825
.target(name: "Extensions"),
2926
.testTarget(name: "ExtensionsTests", dependencies: ["Extensions"]),
3027

28+
.target(name: "Fuse"),
29+
.testTarget(name: "FuseTests", dependencies: ["Fuse"]),
30+
3131
.target(name: "HTTPNetworking"),
3232
.testTarget(name: "HTTPNetworkingTests", dependencies: ["HTTPNetworking"]),
3333

Sources/Extensions/Publisher+Sinks.swift renamed to Sources/Fuse/Extensions/Publisher+Sinks.swift

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -23,41 +23,6 @@
2323
import Foundation
2424
import Combine
2525

26-
// MARK: - DisposableBag
27-
28-
/// A thread safe bag of `AnyCancellables`
29-
public class DisposableBag {
30-
31-
// MARK: Properties
32-
33-
private let lock = NSLock()
34-
private var cancellables = Set<AnyCancellable>()
35-
36-
public var count: Int {
37-
return cancellables.count
38-
}
39-
40-
// MARK: Methods
41-
42-
public func store(_ anyCancellable: AnyCancellable) {
43-
lock.lock()
44-
cancellables.insert(anyCancellable)
45-
lock.unlock()
46-
}
47-
48-
public func dispose(_ anyCancellable: AnyCancellable) {
49-
lock.lock()
50-
cancellables.remove(anyCancellable)
51-
lock.unlock()
52-
}
53-
54-
public func empty() {
55-
lock.lock()
56-
cancellables.removeAll()
57-
lock.unlock()
58-
}
59-
}
60-
6126
// MARK: - Standard Sinks
6227

6328
extension Publisher {
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//
2+
// MIT License
3+
//
4+
// Copyright (c) 2023 Connor Ricks
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
24+
import Combine
25+
import Foundation
26+
27+
/// A thread safe bag of `AnyCancellables`
28+
public class DisposableBag {
29+
30+
// MARK: Properties
31+
32+
private let lock = NSLock()
33+
private(set) var cancellables = Set<AnyCancellable>()
34+
35+
// MARK: Methods
36+
37+
public func store(_ anyCancellable: AnyCancellable) {
38+
lock.lock()
39+
cancellables.insert(anyCancellable)
40+
lock.unlock()
41+
}
42+
43+
public func dispose(_ anyCancellable: AnyCancellable) {
44+
lock.lock()
45+
cancellables.remove(anyCancellable)
46+
lock.unlock()
47+
}
48+
49+
public func empty() {
50+
lock.lock()
51+
cancellables.removeAll()
52+
lock.unlock()
53+
}
54+
}

Tests/ExtensionsTests/PublisherSinksTests.swift renamed to Tests/FuseTests/Extensions/PublisherSinksTests.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121
// SOFTWARE.
2222

23-
@testable import Extensions
23+
@testable import Fuse
2424
import XCTest
2525
import Combine
2626

@@ -38,7 +38,7 @@ class PublisherSinksTests: XCTestCase {
3838

3939
var cancellable: AnyCancellable?
4040

41-
// MARK: Publisher Tests
41+
// MARK: Sink Tests
4242

4343
func test_synchronousSink_doesComplete_whenValuesFinishedPublishing() {
4444
/// Setup expectations
@@ -133,7 +133,7 @@ class PublisherSinksTests: XCTestCase {
133133
func test_disposableSynchronousBagSink_doesCleanup_whenValuesFinishedPublishing() {
134134
/// Prepare test
135135
let bag = DisposableBag()
136-
XCTAssertEqual(bag.count, 0)
136+
XCTAssertEqual(bag.cancellables.count, 0)
137137

138138
/// Setup expectations
139139
let valueExpectation = createReceiveValueExpectation(count: 3)
@@ -157,7 +157,7 @@ class PublisherSinksTests: XCTestCase {
157157
func test_disposableAsynchronousBagSink_doesCleanup_whenValuesFinishedPublishing() {
158158
/// Prepare test
159159
let bag = DisposableBag()
160-
XCTAssertEqual(bag.count, 0)
160+
XCTAssertEqual(bag.cancellables.count, 0)
161161

162162
/// Setup expectations
163163
let valueExpectation = createReceiveValueExpectation(count: 1)
@@ -187,7 +187,7 @@ class PublisherSinksTests: XCTestCase {
187187
func test_disposableBagSink_doesCleanup_whenCancellableIsCancelled() {
188188
/// Prepare test
189189
let bag = DisposableBag()
190-
XCTAssertEqual(bag.count, 0)
190+
XCTAssertEqual(bag.cancellables.count, 0)
191191

192192
/// Setup expectations
193193
let valueExpectation = createReceiveValueExpectation(count: 1)
@@ -217,7 +217,7 @@ class PublisherSinksTests: XCTestCase {
217217
func test_disposableBagSink_doesCleanup_whenErrorIsPublished() {
218218
/// Prepare test
219219
let bag = DisposableBag()
220-
XCTAssertEqual(bag.count, 0)
220+
XCTAssertEqual(bag.cancellables.count, 0)
221221

222222
/// Setup expectations
223223
let valueExpectation = createReceiveValueExpectation(count: 1)
@@ -249,7 +249,7 @@ class PublisherSinksTests: XCTestCase {
249249
func test_disposableBag_whenEmptied_doesCancelAllPublishers() {
250250
/// Prepare test
251251
let bag = DisposableBag()
252-
XCTAssertEqual(bag.count, 0)
252+
XCTAssertEqual(bag.cancellables.count, 0)
253253

254254
/// Setup expectations
255255
let completionExpectation = expectation(description: "Expected publisher to complete.")
@@ -266,7 +266,7 @@ class PublisherSinksTests: XCTestCase {
266266
}, bag: bag)
267267

268268
bag.empty()
269-
XCTAssertEqual(bag.count, 0)
269+
XCTAssertEqual(bag.cancellables.count, 0)
270270

271271
wait(for: [
272272
completionExpectation,
@@ -281,7 +281,7 @@ private extension PublisherSinksTests {
281281
func createEmptyBagExpectation(from bag: DisposableBag) -> XCTestExpectation {
282282
return expectation(for: NSPredicate { any, _ in
283283
guard let bag = any as? DisposableBag else { return false}
284-
return bag.count == 0
284+
return bag.cancellables.count == 0
285285
}, evaluatedWith: bag, handler: .none)
286286
}
287287

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
"parallelizable" : true,
1717
"target" : {
1818
"containerPath" : "container:",
19-
"identifier" : "BuffableAsyncPublishersTests",
20-
"name" : "BuffableAsyncPublishersTests"
19+
"identifier" : "FuseTests",
20+
"name" : "FuseTests"
2121
}
2222
}
2323
],
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// MIT License
2+
//
3+
// Copyright (c) 2023 Connor Ricks
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in all
13+
// copies or substantial portions of the Software.
14+
//
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
// SOFTWARE.
22+
23+
@testable import Fuse
24+
import XCTest
25+
import Combine
26+
27+
class DisposableBagTests: XCTestCase {
28+
29+
// MARK: MockCancellable
30+
struct MockCancellable: Cancellable {
31+
let id: Int
32+
func cancel() {}
33+
}
34+
35+
// MARK: Tests
36+
37+
func test_disposableBag_whenStoringAndDisposingCancellable_doesUpdateCancellableSet() {
38+
let bag = DisposableBag()
39+
let cancellableOne = AnyCancellable(MockCancellable(id: 1))
40+
let cancellableTwo = AnyCancellable(MockCancellable(id: 2))
41+
42+
bag.store(cancellableOne)
43+
XCTAssertTrue(bag.cancellables == [cancellableOne])
44+
45+
bag.store(cancellableTwo)
46+
XCTAssertTrue(bag.cancellables == [cancellableOne, cancellableTwo])
47+
48+
bag.dispose(cancellableTwo)
49+
XCTAssertTrue(bag.cancellables == [cancellableOne])
50+
51+
bag.dispose(cancellableOne)
52+
XCTAssertTrue(bag.cancellables == [])
53+
}
54+
55+
func test_disposableBag_whenEmptying_doesRemoveAllCancellables() {
56+
let bag = DisposableBag()
57+
let cancellableOne = AnyCancellable(MockCancellable(id: 1))
58+
let cancellableTwo = AnyCancellable(MockCancellable(id: 2))
59+
60+
bag.store(cancellableOne)
61+
bag.store(cancellableTwo)
62+
63+
bag.empty()
64+
XCTAssertTrue(bag.cancellables == [])
65+
}
66+
67+
func test_disposableBag_whenDisposingCancellableNotInBag_doesNotAlterBag() {
68+
let bag = DisposableBag()
69+
let cancellableOne = AnyCancellable(MockCancellable(id: 1))
70+
let cancellableTwo = AnyCancellable(MockCancellable(id: 2))
71+
let cancellableThree = AnyCancellable(MockCancellable(id: 3))
72+
73+
bag.store(cancellableOne)
74+
bag.store(cancellableTwo)
75+
76+
bag.dispose(cancellableThree)
77+
XCTAssertTrue(bag.cancellables == [cancellableOne, cancellableTwo])
78+
}
79+
}

Tests/BuffableAsyncPublishersTests/BuffableAsyncPublisherTests.swift renamed to Tests/FuseTests/Publishers/BuffableAsyncPublisherTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121
// SOFTWARE.
2222

23-
@testable import BuffableAsyncPublishers
23+
@testable import Fuse
2424
import Combine
2525
import XCTest
2626

0 commit comments

Comments
 (0)