iOS 개발자 되기/combine

combine (1) - Publisher, Subscriber

순진이 2022. 11. 3. 17:00

Publisher Protocol

public protocol Publisher<Output, Failure> {

    associatedtype Output
    associatedtype Failure : Error
}

✔️ 말 그대로 Publish(발행)하는 프로토콜. Subscriber의 instance에게 elements를 전달. 

✔️ Publisher의 associated types인 Output, Failure는 -> Subscriber의 associated types인 Input, Failure과 매치됨

✔️ Publisher는 subscriber를 받아들이기 위해 receive(subscriber:)를 함(Publisher와 Subscriber가 연결되는 행위).

✔️ 그래야 Publisher가 발행 하는 값을 Subscriber가 받을 수 있음.

 

 

이후, publisher는 subscriber가 가진 아래 3가지 메서드를 호출할 수 있음.

 

 

2. 나만의 Publisher 만들기도 가능 (아직은 무슨 말인지 모르겠음)

✔️ send(_:) 메서드를 호출하여 필요에 따라 값을 게시하려면 PassthroughSubject와 같은 제목의 구체적인 서브 클래스를 사용

✔️ 값을 업데이트할 때마다 publish 하기 위해 CurrentValueSubject 사용

✔️ 프로퍼티 앞에 @Published 키워드를 붙이면, 그 프로퍼티의 값이 변할 때마다 이벤트를 방출하는 publisher를 얻을 수 있음


Subcriber Protocol

✔️ Publisher가 방출한 값(a stream of elements)을 받는 주체

✔️ Subscriber의 Input, Failure는 Publisher의 Output, Failure와 매칭되어야 함

✔️ Publisher과 연결되기 위해서는 해당 publisher의 subscribe(_:) 메서르를 호출해야 함

public func subscribe<S>(_ subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input

✔️ 이 메서드를 호출하면 publisher는 subscriber의 1️⃣ receive(subscription:)를 실행시키는데, 이 때  subscription을 받게 됨. (subscription이 있어야 원하는 요구를 publisher한테 할 수 있게 됨)

✔️ subscriber가 첫 번째 요구(demand)를 한 후에 publisher는 2️⃣receive(_:)를 호출

✔️ 그리고 발행이 끝나면, 3️⃣receive(completion:)를 호출

public protocol Subscriber<Input, Failure> : CustomCombineIdentifierConvertible {

    associatedtype Input
    associatedtype Failure : Error


    func receive(subscription: Subscription)

    func receive(_ input: Self.Input) -> Subscribers.Demand

    func receive(completion: Subscribers.Completion<Self.Failure>)
}

 

Subcribers.Demand

구독권(subscription)을 통해 구독자(subscriber)가 발행자(publisher)에게 요청한 항목의 수. 

 

 

Subcribers.Completion

publisher가 더 이상 elements를 생성하지 않겠다는 뜻!

왜냐? 발행이 정상적으로 완료되었거나 error로 끝났기 때문에


 

예시

string값을 받는 subscriber 만들기 (Subscriber 프로토콜 채택)

class StringSubscriber: Subscriber {
    typealias Input = String
    typealias Failure = Never
    
    func receive(subscription: Subscription) {
        print("Received Subscription")
    }
    
    func receive(_ input: String) -> Subscribers.Demand {
        print("Recived Value: ", input)
        return .none
    }
    
    func receive(completion: Subscribers.Completion<MyError>) {
        print("Completed")
    }
}

 

subscriber가 구독(subcribe) 시작

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let publisher = ["1", "2", "3", "4", "5"].publisher
        let subscriber = StringSubscriber() // 나는 구독자
        publisher.subscribe(subscriber)
        
    }
}

 

 

subscriber 이놈이 나를 구독했구만? 그러면 구독권(Subcription)을 줄게!

-> receive(subscription:) 메서드가 호출되며, subcription을 받음.

 

 

그런데, 이렇게만 하고 print문을 찍어보면 아래와 같이 Subscription을 받은 것만 나옴

Received Subscription

 

🤦🏻‍♀️ 왜 주는데 받지를 못하니?

 

얻고 싶은 게 있다면, 받은 subscription을 가지고 원하는 것을 요청(request)해야 함.

func receive(subscription: Subscription) {
    print("Received Subscription")
    subscription.request(.max(1)) // backpressure - 1개의 결과만 받겠다! 네가 가진 게 100개라도 난 3개만 필요해
}

 

 

이렇게만 하고 print문을 찍어보면 아래와 같이 Recived Value가 1만 출력됨

Received Subscription
Recived Value:  1

 

 

.unlimited로 설정하니 모든 값을 다 받을 수 있었음!

Completed는 아래처럼 1️⃣모든 값을 다 받았을 때 (여기서는 ["1", "2", "3", "4", "5"])

2️⃣error의 결과일 때 불려짐.

class StringSubscriber: Subscriber {

    
    typealias Input = String
    typealias Failure = Never
    
    func receive(subscription: Subscription) {
        print("Received Subscription")
        subscription.request(.unlimited) 
    }
    
    func receive(_ input: String) -> Subscribers.Demand {
        print("Recived Value: ", input)
        return .none
    }
    
    func receive(completion: Subscribers.Completion<Never>) {
        print("Completed")
    }
}

 

또한 receive(subscription:)에서 max(1)값을 요구했더라도,

receive(:)에서 요구를 수정할 수 있음.

class StringSubscriber: Subscriber {

    
    typealias Input = String
    typealias Failure = Never
    
    func receive(subscription: Subscription) {
        print("Received Subscription")
        subscription.request(.max(1)) // backpressure - 3개의 결과만 받겠다! 네가 가진 게 100개라도 난 3개만 필요해
    }
    
    func receive(_ input: String) -> Subscribers.Demand {
        print("Recived Value: ", input)
        return .max(1)
    }
    
    func receive(completion: Subscribers.Completion<Never>) {
        print("Completed")
    }
}

출력되는 결과

Received Subscription
Recived Value:  1
Recived Value:  2
Recived Value:  3
Recived Value:  4
Recived Value:  5
Completed

 


출처

-https://developer.apple.com/documentation/combine/subscriber

 

Apple Developer Documentation

 

developer.apple.com

- https://nsios.tistory.com/181

 

[Swift] Combine의기본 Publisher이란?

예전에 Published를 스유를 기준으로 다뤘다면 이번에는 비동기를 기준으로 다룰 Publisher에 대해 알아보려고해요 둘이 다른건 아니에요! 스위프트 API에서 이미 많은 publisher를 지원하고있어요 이렇

nsios.tistory.com

https://zeddios.tistory.com/966

 

Combine (1-1) - Subcribers.Demand

안녕하세요 :) Zedd입니다. https://zeddios.tistory.com/925 Combine (1) - Publisher, Subscriber 안녕하세요 :) Zedd입니다. 이번 휴가동안 SwiftUI를 좀 해볼라하는데 Combine모르면 이거 걍 노답임.... -> 휴가 끝나버렸

zeddios.tistory.com