import UIKit
import Combine
var greeting = "Hello, playground"
var subscriptions = Set<AnyCancellable>()
func example(of name: String, closure: () -> Void) {
print("--------------------------\(name)--------------------------")
closure()
}
example(of: "prepend(Output...)") {
let publisher = [3,4].publisher
publisher
.prepend(1,2)
.prepend(-1,0)
.sink(receiveValue: {print($0)})
.store(in: &subscriptions)
}
example(of: "prepend(Sequence)") {
let publisher = [5,6,7].publisher
publisher
.prepend([3,4])
.prepend(Set(1...2)) // can be printed as 1,2 or 2,1
.prepend(stride(from: 6, to: 12, by: 2)) // stride: 보폭, not including an end value
.sink(receiveValue: {print($0)})
.store(in: &subscriptions)
}
// stride : Returns a sequence from a starting value to, but not including, an end value, stepping by the specified amount.
example(of: "prepend(Publisher)") {
let publisher1 = [3,4].publisher
let publisher2 = [1,2].publisher
publisher1
.prepend(publisher2)
.sink(receiveValue: {print($0)})
.store(in: &subscriptions)
}
example(of: "prepend(Publisher) #2") {
let publisher1 = [3,4].publisher
let publisher2 = PassthroughSubject<Int, Never>()
publisher1
.prepend(publisher2)
.sink(receiveValue: {print($0)})
.store(in: &subscriptions)
publisher2.send(1)
publisher2.send(2)
publisher2.send(3)
publisher2.send(completion: .finished)
}
// PassthroughSubject: you can push values to manually.
example(of: "append") {
let publisher = [1,2].publisher
publisher
.append(3,4)
.sink(receiveValue: {print($0)})
.store(in: &subscriptions)
}
example(of: "append #2") {
let publisher1 = [1].publisher
let publisher2 = PassthroughSubject<Int, Never>()
publisher2
.append(publisher1)
.sink(receiveValue: {print($0)})
.store(in: &subscriptions)
publisher2.send(2)
publisher2.send(3)
publisher2.send(completion: .finished)
}
example(of: "append(Sequence)") {
let publisher = [1, 2, 3].publisher
publisher
.append([4, 5])
.append(Set([6, 7]))
.append(stride(from: 8, to: 11, by: 2)) // 4
.sink(receiveValue: { print($0) })
.store(in: &subscriptions)
}
example(of: "append(publisher)") {
let publisher1 = [1,2].publisher
let publisher2 = [3,4].publisher
publisher2
.append(publisher1)
.sink(receiveValue: {print($0)})
.store(in: &subscriptions)
}
example(of: "switchToLatest") {
let publisher1 = PassthroughSubject<Int, Never>()
let publisher2 = PassthroughSubject<Int, Never>()
let publisher3 = PassthroughSubject<Int, Never>()
// Create a second PassthroughSubject that accepts other PassthroughSubjects
let publishers = PassthroughSubject<PassthroughSubject<Int, Never>, Never>()
publishers
// Use switchToLatest on your publishers. Now, every time you send a different publisher through the publishers subject, you switch to the new one and cancel the previous subscription.
.switchToLatest()
.sink(receiveCompletion: { _ in print("Completed!")}, receiveValue: { print($0)})
.store(in: &subscriptions)
// Send publisher1 to publishers and then send 1 and 2 to publisher1.
publishers.send(publisher1)
publisher1.send(1)
publisher1.send(2)
// Send publisher2, which cancels the subscription to publisher1. You then send 3 to publisher1, but it’s ignored, and send 4 and 5 to publisher2, which are pushed through because publisher2 is the current subscription.
publishers.send(publisher2)
publisher1.send(3)
publisher2.send(4)
publisher2.send(5)
publishers.send(publisher3)
publisher2.send(6)
publisher3.send(7)
publisher3.send(8)
publisher3.send(9)
// Finally, you send a completion event to the current publisher, publisher3, and another completion event to publishers. This completes all active subscriptions.
publisher3.send(completion: .finished)
publishers.send(completion: .finished)
}
//example(of: "switchToLatest - Network Request") {
// let url = URL(string: "https://source.unsplash.com/random")!
// // 1
// func getImage() -> AnyPublisher<UIImage?, Never> {
//
// return URLSession.shared
// .dataTaskPublisher(for: url)
// // 2 Create a PassthroughSubject to simulate user taps on a button.
// .map { data, _ in UIImage(data: data) }
// .print("image")
// .replaceError(with: nil)
// .eraseToAnyPublisher()
// }
// let taps = PassthroughSubject<Void, Never>()
//
// taps
// .map { _ in getImage() } // 3
// .switchToLatest() // 4
// .sink(receiveValue: { _ in })
// .store(in: &subscriptions)
// // 5
// taps.send()
//
// DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
// print("first tap has sended")
// taps.send()
// }
// DispatchQueue.main.asyncAfter(deadline: .now() + 3.1) {
// print("second tap has sended")
// taps.send()
// }
//}
//// 3 : Upon a button tap, map the tap to a new network request for a random image by calling getImage(). This essentially transforms Publisher<Void, Never> into Publisher<Publisher<UIImage?, Never>, Never> — a publisher of publishers.
//
//// 4 : Use switchToLatest() exactly like in the previous example, since you have a publisher of publishers. This guarantees only one publisher will emit values, and cancel any previous subscriptions.
//
//// 5 : Simulate three delayed button taps using a DispatchQueue. The first tap is immediate, the second tap comes after three seconds, and the last tap comes just a tenth of a second after the second tap.
example(of: "merge(with:)") {
let publisher1 = PassthroughSubject<Int, Never>()
let publisher2 = PassthroughSubject<Int, Never>()
publisher1
.merge(with: publisher2)
.sink(receiveCompletion: { _ in print("Completed")}, receiveValue: {print($0)})
.store(in: &subscriptions)
publisher1.send(1)
publisher1.send(2)
publisher2.send(3)
publisher1.send(4)
publisher2.send(5)
// for publisher1 to be finished, both of them should be done.
publisher1.send(completion: .finished)
publisher2.send(completion: .finished)
}
example(of: "combineLatest") {
let publisher1 = PassthroughSubject<Int, Never>()
let publisher2 = PassthroughSubject<String, Never>()
publisher1
.combineLatest(publisher2)
.sink(receiveCompletion: { _ in print("Completed")}, receiveValue: {print("P1: \($0) P2: \($1)")})
.store(in: &subscriptions)
publisher1.send(1)
publisher1.send(2)
publisher2.send("a")
publisher2.send("b")
publisher1.send(3)
publisher2.send("c")
publisher1.send(completion: .finished)
publisher2.send(completion: .finished)
}
example(of: "zip") {
let publisher1 = PassthroughSubject<Int, Never>()
let publisher2 = PassthroughSubject<String, Never>()
publisher1
// .combineLatest(publisher2)
.zip(publisher2)
.sink(receiveCompletion: { _ in print("Completed")}, receiveValue: {print("P1: \($0) P2: \($1)")})
.store(in: &subscriptions)
publisher1.send(1)
publisher1.send(2)
publisher2.send("a")
publisher2.send("b")
publisher1.send(3)
publisher2.send("c")
publisher2.send("d")
publisher1.send(completion: .finished)
publisher2.send(completion: .finished)
}
/*
Key points
In this chapter, you learned how to take different publishers and create meaningful combinations with them. More specifically, you learned that:
• You can use the prepend and append families of operators to add emissions from one publisher before or after the original publisher.
• While switchToLatest is relatively complex, it’s extremely useful. It takes a publisher that emits publishers, switches to the latest publisher and cancels the subscription to the previous publisher.
• merge(with:) lets you interleave values from multiple publishers.
• combineLatest emits the latest values of all combined publishers whenever any of them emit a value, once all of the combined publishers have emitted at least one value.
• zip pairs emissions from different publishers, emitting a tuple of pairs after all publishers have emitted an value.
• You can mix combination operators to create interesting and complex relationships between publishers and their emissions.
*/