Skip to content

Commit

Permalink
OR-5257 FilteringOperators:: Dropping values (#92)
Browse files Browse the repository at this point in the history
  • Loading branch information
crazymanish authored Jul 9, 2023
1 parent 7813079 commit e10ab65
Show file tree
Hide file tree
Showing 3 changed files with 214 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CombineDemo/CombineDemo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
966784792A5B057F00398D70 /* CompactMapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966784782A5B057F00398D70 /* CompactMapTests.swift */; };
9667847B2A5B09DC00398D70 /* IgnoreOutputTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9667847A2A5B09DC00398D70 /* IgnoreOutputTests.swift */; };
9667847D2A5B27A800398D70 /* FindingValuesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9667847C2A5B27A800398D70 /* FindingValuesTests.swift */; };
9667847F2A5B302E00398D70 /* DroppingValuesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9667847E2A5B302E00398D70 /* DroppingValuesTests.swift */; };
967AF3A92A485C3100AB60CA /* PassthroughSubjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 967AF3A82A485C3100AB60CA /* PassthroughSubjectTests.swift */; };
967AF4B92A526E0600AB60CA /* CurrentValueSubjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 967AF4B82A526E0600AB60CA /* CurrentValueSubjectTests.swift */; };
967C6CB62A5AC96300461FF3 /* ReplaceNilTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 967C6CB52A5AC96300461FF3 /* ReplaceNilTests.swift */; };
Expand Down Expand Up @@ -110,6 +111,7 @@
966784782A5B057F00398D70 /* CompactMapTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompactMapTests.swift; sourceTree = "<group>"; };
9667847A2A5B09DC00398D70 /* IgnoreOutputTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IgnoreOutputTests.swift; sourceTree = "<group>"; };
9667847C2A5B27A800398D70 /* FindingValuesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindingValuesTests.swift; sourceTree = "<group>"; };
9667847E2A5B302E00398D70 /* DroppingValuesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DroppingValuesTests.swift; sourceTree = "<group>"; };
967AF3A82A485C3100AB60CA /* PassthroughSubjectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassthroughSubjectTests.swift; sourceTree = "<group>"; };
967AF4B82A526E0600AB60CA /* CurrentValueSubjectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentValueSubjectTests.swift; sourceTree = "<group>"; };
967C6CB52A5AC96300461FF3 /* ReplaceNilTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplaceNilTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -148,6 +150,7 @@
966784782A5B057F00398D70 /* CompactMapTests.swift */,
9667847A2A5B09DC00398D70 /* IgnoreOutputTests.swift */,
9667847C2A5B27A800398D70 /* FindingValuesTests.swift */,
9667847E2A5B302E00398D70 /* DroppingValuesTests.swift */,
);
path = FilteringOperators;
sourceTree = "<group>";
Expand Down Expand Up @@ -491,6 +494,7 @@
9642B78029D2876200CB89C8 /* FutureTests.swift in Sources */,
9612D6B02A5ACE06007CBD1A /* ReplaceEmptyTests.swift in Sources */,
9642B7A729D369A600CB89C8 /* ApiError.swift in Sources */,
9667847F2A5B302E00398D70 /* DroppingValuesTests.swift in Sources */,
9631D29929D70D6A00A9D790 /* DefaultErrorTests.swift in Sources */,
9642B7F729D4C54600CB89C8 /* FailTests.swift in Sources */,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
//
// DroppingValuesTests.swift
// CombineDemoTests
//
// Created by Manish Rathi on 09/07/2023.
//

import Foundation
import Combine
import XCTest

/// - We can use it when we want to ignore values from one publisher until a second one starts publishing,
/// - or if we want to ignore a specific amount of values at the start of the stream.
///
/// - `dropFirst(_:)` Omits the specified number of elements before republishing subsequent elements.
/// - https://developer.apple.com/documentation/combine/publishers/collect/dropfirst(_:)
/// - `drop(while:)` Omits elements from the upstream publisher until a given closure returns false, before republishing all remaining elements.
/// - https://developer.apple.com/documentation/combine/publishers/collect/drop(while:)
/// - `tryDrop(while:)` Omits elements from the upstream publisher until an error-throwing closure returns false, before republishing all remaining elements.
/// - https://developer.apple.com/documentation/combine/publishers/collect/trydrop(while:)
/// - `drop(untilOutputFrom:)` Ignores elements from the upstream publisher until it receives an element from a second publisher.
/// - https://developer.apple.com/documentation/combine/publishers/collect/drop(untiloutputfrom:)
final class DroppingValuesTests: XCTestCase {
var publisher: Publishers.Sequence<ClosedRange<Int>, Never>!
var cancellables: Set<AnyCancellable>!
var isFinishedCalled: Bool!

override func setUp() {
super.setUp()

publisher = (1...10).publisher
cancellables = []
isFinishedCalled = false
}

override func tearDown() {
publisher = nil
cancellables = nil
isFinishedCalled = nil

super.tearDown()
}

func testPublisherWithDropFirstOperator() {
// Given: Publisher
// publisher = (1...10).publisher
var receivedValues: [Int] = []

// When: Sink(Subscription)
publisher
.dropFirst()
.sink { [weak self] completion in
switch completion {
case .finished:
self?.isFinishedCalled = true
}
} receiveValue: { value in
receivedValues.append(value)
}
.store(in: &cancellables)

// Then: Receiving correct value
XCTAssertTrue(isFinishedCalled)
XCTAssertEqual(receivedValues, [2, 3, 4, 5, 6, 7, 8, 9, 10]) // drop (first)
}

func testPublisherWithDropFirstWithSpecificCountOperator() {
// Given: Publisher
// publisher = (1...10).publisher
var receivedValues: [Int] = []

// When: Sink(Subscription)
publisher
.dropFirst(6)
.sink { [weak self] completion in
switch completion {
case .finished:
self?.isFinishedCalled = true
}
} receiveValue: { value in
receivedValues.append(value)
}
.store(in: &cancellables)

// Then: Receiving correct value
XCTAssertTrue(isFinishedCalled)
XCTAssertEqual(receivedValues, [7, 8, 9, 10]) // drop (first) 6 elements
}

func testPublisherWithDropWhileOperator() {
// Given: Publisher
// publisher = (1...10).publisher
var receivedValues: [Int] = []

// When: Sink(Subscription)
publisher
.drop(while: { $0 % 3 != 0 })
.sink { [weak self] completion in
switch completion {
case .finished:
self?.isFinishedCalled = true
}
} receiveValue: { value in
receivedValues.append(value)
}
.store(in: &cancellables)

// Then: Receiving correct value
XCTAssertTrue(isFinishedCalled)
XCTAssertEqual(receivedValues, [3, 4, 5, 6, 7, 8, 9, 10]) // drop (while) [elements % 3 != 0]
}

func testPublisherWithTryDropWhileOperator() {
// Given: Publisher
let publisher = [1, 2, 3, 4, 5, 6, -1, 7, 8, 9, 10].publisher
let range: CountableClosedRange<Int> = (1...100)
var receivedValues: [Int] = []
var receivedError: ApiError?

// When: Sink(Subscription)
publisher
.tryDrop {
guard $0 != 0 else { throw ApiError(code: .notFound) }
return range.contains($0)
}
.sink { [weak self] completion in
switch completion {
case .finished:
self?.isFinishedCalled = true
case .failure(let error):
receivedError = error as? ApiError
}
} receiveValue: { value in
receivedValues.append(value)
}
.store(in: &cancellables)

// Then: Receiving correct value
XCTAssertTrue(isFinishedCalled) // Successful finished
XCTAssertEqual(receivedValues, [-1, 7, 8, 9, 10])
XCTAssertNil(receivedError)
}

func testPublisherWithTryDropWhileOperatorScenario2() {
// Given: Publisher
let publisher = [1, 2, 3, 4, 5, 6, 0, -1, 7, 8, 9, 10].publisher
let range: CountableClosedRange<Int> = (1...100)
var receivedValues: [Int] = []
var receivedError: ApiError?

// When: Sink(Subscription)
publisher
.tryDrop {
guard $0 != 0 else { throw ApiError(code: .notFound) }
return range.contains($0)
}
.sink { [weak self] completion in
switch completion {
case .finished:
self?.isFinishedCalled = true
case .failure(let error):
receivedError = error as? ApiError
}
} receiveValue: { value in
receivedValues.append(value)
}
.store(in: &cancellables)

// Then: Receiving correct value
XCTAssertFalse(isFinishedCalled) // Successful finished not called, because it is finished with error
XCTAssertEqual(receivedValues, []) // Nothing received because of error
XCTAssertEqual(receivedError?.code, .notFound) // Finished with correct error
}

func testPublisherWithTryDropUntilOutputFromOperator() {
// Given: Publisher
let firstPublisher = PassthroughSubject<Int,Never>()
let secondPublisher = PassthroughSubject<String,Never>()
var receivedValues: [Int] = []

// When: Sink(Subscription)
firstPublisher
.drop(untilOutputFrom: secondPublisher)
.sink { [weak self] completion in
switch completion {
case .finished:
self?.isFinishedCalled = true
}
} receiveValue: { value in
receivedValues.append(value)
}
.store(in: &cancellables)

// It will drop all values of firstPublisher, until secondPublisher kicks-in
firstPublisher.send(1)
firstPublisher.send(2)
secondPublisher.send("This will kick-off the firstPublisher")
firstPublisher.send(3)
firstPublisher.send(4)
firstPublisher.send(completion: .finished)

// Then: Receiving correct value
XCTAssertTrue(isFinishedCalled) // Successful finished
XCTAssertEqual(receivedValues, [3, 4]) // Received values only after the secondPublisher's output
}
}
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@
- [x] Finding values
- `first() first(where:) tryFirst(where:)` https://github.com/crazymanish/what-matters-most/pull/91
- `last() last(where:) tryLast(where:)` https://github.com/crazymanish/what-matters-most/pull/91
- [ ] Droping values
- [x] Droping values
- `dropFirst(_:)` https://github.com/crazymanish/what-matters-most/pull/92
- `drop(while:) tryDrop(while:)` https://github.com/crazymanish/what-matters-most/pull/92
- `drop(untilOutputFrom:)` https://github.com/crazymanish/what-matters-most/pull/92
- [ ] Limiting values
- [ ] Practices
- [ ] Combining Operators
Expand Down

0 comments on commit e10ab65

Please sign in to comment.