iOS 개발/Combine

[Combine] combine 정리2 (sink, handleEvents, receive)

bamtorii 2023. 6. 20. 16:42
반응형
  1. sink
  2. handleEvents(receiveOutput: )
  3. receive(on: ), subscribe(on: )

1. sink

  • 애플 개발자 문서를 보면 sink 란 클로저 기반의 동작이 있는 subscriber를 절대 실패하지 않게 하는 publisher와 연결하는 것
  • 그렇기 때문에 에러 타입은 Never 일때만 가능(그래서 회사 subject 에러 타입이 죄다 Never인 것)
func sink(receiveValue: @escaping ((Self.Output) -> Void)) -> AnyCancellable

 

Return Value

  • 수신 된 값의 할당을 종료할 때 사용하는 Cancellable 인스턴스를 리턴한다. 결과를 할당해제 한다면 sumbscription stream을 해제하게 된다.

 

parameter

  • receiveValue: 값을 받을 때 실행하는 클로저
  • receiveCompletion: stream이 성공적으로 종료되었음을 알려주는 클로저(구독이 종료됨을 알려주는 클로저)

 

2. handleEvents(receiveOutput:)

handleEvents()의 경우에는 publisher event가 발생할 때마다 수행된다.

그래서 UI 업데이트, 로킹 등의 부작용을 수행할 때마다 사용할 수 있는 operator라고 한다. 

 

publisher의 라이프사이클에 있는 모든 이벤트를 가로채고, 각 단계에서 조치를 취할 수 있다.

뭐가 문젠지 모르겠을때 handleEvents를 사용해서 무슨 일이 일어나고 있는지 추적할 수 있다.

 

3. receive(on: ) & subscribe(on: )

publisher

subscriber

subject

scheduler

cancellable

 

receive에 대해 알아보기 전에 scheduler에 대해 먼저 살펴보겠다.

 

Scheduler

scheduler는 프로토콜이다. 

scheduler는 closure의 실행시기와 방법을 정의하는 프로토콜이다.

이 scheduler를 이용해서 가능한 빨리 코드를 실행시킬 수도 있고, 특정 시간 이후에 코드를 실행시킬 수도 있다.

 

우리가 publisher, subscriber, subject를 공부할 때 scheduler에 대한 코드는 전혀 없었다.

그 이유는 combine에서는 따로 scheduler를 지정하지 않더라도 기본 scheduler를 제공하기 때문이다.

 

scheduler는 element가 생성된 스레드와 동일한 스레드를 사용한다.

let subject = PassthroughSubject<Int, Never>()

let token = subject.sink(receiveValue: { value in
      print(Thread.isMainThread) // true
})

subject.send(1)

기본적으로 main thread에서 일어나기 때문에 true가 출력된다.

 

그렇다면 background 스레드(global) 에서 element를 발행(send를 하는 곳)하면 background 스레드에서 element를 받는지를 확인해보자

let subject = PassthroughSubject<Int, Never>()
// 1
let token = subject.sink(receiveValue: { value in
    print(Thread.isMainThread)
})
// 2
subject.send(1)
// 3
DispatchQueue.global().async {
    subject.send(2)
}

// true
// false

2번째에서 subject에 그냥 send를 보내면 true가 출력된다.

3번째에서 global에 감싸 send를 보내게 되면 false가 출력된다.

 

이렇게 Combine의 기본 Scheduler를 봤으니 Combine에서 어떻게 Scheduler를 명시적으로 지정해주는지 보도록 하자.

  • receive(on: )
  • subscribe(on: )

 

receive(on: )

먼저 receive(on: )을 보도록 하자.

receive(on: )은 publisher로 부터 element를 수신한 scheduler를 지정하는 역할을 한다.

정확히 말하면 downstream의 실행 컨텍스트를 변경하는 역할을 한다.

 

receive(on: ) 예제

let publisher = ["Zedd"].publisher

publisher
    .map { _ in print(Thread.isMainThread) } // true
    .receive(on: DispatchQueue.global()) 
    .map { print(Thread.isMainThread) } // false
    .sink { print(Thread.isMainThread) } // false

앞서 receive(on: )은 "publisher로 부터 element를 수신할 scheduler를 지정하는 역할"이라고 했다.

 

원래 기본적으로 element가 생성된 스레드가 MainThread여서 첫번째 map의 결과로 true가 출력된다.

그 후 receive(on: )을 활용해 global 스레드로 변경해주었고, 그 결과 두번째 map의 결과로는 false가 출력된다.

 

*참고

receive(on: ), subscribe(on: ) 은 파라미터 타입으로 Scheduler 타입을 받는다.

Scheduler 타입의 종류에는 DispatchQueue, OperationQueue, RunLoop, ImmediateScheduler가 있다.

 

subscribe(on: )

subscribe(on: )은 subsribe, cancel, request operation을 수행할 scheduler를 지정하는 역할을 한다.

위에서 언급했듯이, upstream의 실행 컨텍스트를 변경한다.

(일단 회사 코드에서 subscribe(on: )은 사용을 잘 안해서 예제는 생략하겠다. 그리고 보니까 receive랑 별 차이를 모르겠달까..)

 

참고링크:

https://zeddios.tistory.com/972

 

Combine (3) - Scheduler

안녕하세요 :) Zedd입니다. Publisher와 Subscriber, Subject를 공부했는데요, 오늘은 Scheduler를 공부해볼게요. Publisher Subscriber Subject Scheduler Cancellable 역시나 프로토콜입니다. Scheduler Scheduler는 closure의 실

zeddios.tistory.com

https://velog.io/@sossont/%EB%8F%99%EA%B8%B0%EC%99%80-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%9D%98-%EC%B0%A8%EC%9D%B4

 

[Swift] 동기/비동기, 직렬/동시의 차이

iOS 프로그래머라면 꼭 이해해야하는 개념들.

velog.io

handleevents와 sink의 차이 참고링크: 

https://sujinnaljin.medium.com/combine-debugging-2c045cdd2ce3

 

[Combine] Debugging

뚝딱따리뚝딱딱 디버깅을 해보자 🛠

sujinnaljin.medium.com

 

반응형