Combine

Combine_Chap5_CombiningOperators

iosswift 2021. 11. 26. 17:42
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.
*/