본문 바로가기

iOS공통

[Swift] Combine 2편: publisher이해하고 사용하기 with Subject

 

안녕하세요✋ 주니오스입니다.

 

 

오늘은 Combine 2편입니다.

 

 

Combin의 두 기둥중 하나인 publisher(이하 pub)를 만드는 프로토콜에대해 자세하게 알아봅시다.

 

 

Publisher Protocol

 

 

먼저 Apple doc에 적힌 설명을 읽어봅시다.

 

 

Declares that a type can transmit a sequence of values over time.

 

 

앞의 편에서 언급했듯이 sequence of values즉 일련의 값들을 전송하는 타입을 선언한다.

 

 

Publisher프로토콜을 채택하는 타입은 receive(subscriber:) 메서드를 사용하여 sub를 등록할 수 있습니다.

 

 

이때 pub, sub는 서로 Input, Output, Failure연관타입이 일치해야 합니다.

 

 

더 깊게 들어기 전에 여기서 잠깐✋

 

 

doc에 이런말이 적혀 있더군요

 

 

Rather than implementing the Publisher protocol yourself, you can create your own publisher by using one of several types provided by the Combine framework: Subject

 

 

Publisher프로토콜을 사용하여 pub를 직접만들기 보단 Combine 프레임워크에서 제공하는 pub를 사용해라

 

 

그렇습니다. 우리는 직접 pub를 구현할 필요가 없었습니다!

 

 

Combine프레임워크에는 Subject프로토콜이 있습니다. 이 프로토콜은 Publisher를 채택하고 있어요.

 

 

그래서 Subject를 사용해라? 그것도 아닙니다.🤔

 

 

Subject를 채택하는 두가지 빌트인 클래스가 존재하는데 그것을 사용하면 됩니다.🤗

 

 

CurrentValueSubject, PassthroughSubject 클래스 타입 입니다!

 

먼저 CurrentValueSubject부터 보시죠

 

 

이 타입은 2가지 제네릭 타입과 element의 초기값을 요구합니다.

 

let currentValueSubject = CurrentValueSubject<String, Never>("")

 

감이 오시나요?

 

 

첫번째는 element의 타입(Output) 두번째는 Failure타입입니다.

 

 

pub답게 sink를 사용할 수 있으며 value프로퍼티를 통해 element값을 변경 할 수 있습니다.

 

let currentValueSubject = CurrentValueSubject<String, Never>("")

currentValueSubject.sink { print($0) }

currentValueSubject.value = "Hello"

currentValueSubject.send("World")

print(currentValueSubject.value)


///Hello
///World
///World

 

value프로퍼티에 직접 접근하여 값을 변경하는 경우에도 publish를 진행합니다.

 

 

CurrentValueSubject는 send 인스턴스 매서드를 사용하여 element를 publish할 수 도 있습니다.

 

 

print를 통해 출력된 부분을 봐주시가 바랍니다.

 

 

여기서 이 타입의 큰 장점이 나오는 데요 이렇게 publish된 element는 value프로퍼티에 저장됩니다.

 

 

 

다음으로는 PassthroughSubject입니다.

 

 

이 타입은 CurrentValueSubject와 달리 값을 저장하지 않습니다.

 

 

그저 send를 통해 publish할 뿐입니다.

 

 

그래서 생성시 element의 초기값을 전달받지 않습니다.

 

let passthroughSubject = PassthroughSubject<String, Never>()

passthroughSubject.sink { print($0) }

passthroughSubject.send("Hello world")

print(passthroughSubject.values)


///Hello world

 

하지만, print부분을 보시면 값을 출력하는 것을 확인할 수 있죠? 그런데 자세히 보시면 value가 아니라 values라는 것을 알 수 있습니다.

 

 

여러분들은 AsyncSequence를 아시는가요?

 

 

모르시다면 한번 알아보는 것을 추천합니다. 비동기로 Sequence를 구성하게 하는 프로토콜 입니다.

 

 

비동기로 Sequence를 구성, 비동기로 일련의 값들을 전달 받음 뭔가 Combine과 AsyncSequence는 유사해 보입니다.

 

 

그래서 친절하게도 Apple은 Subject가 values 프로퍼티를 통해 AsyncSequence타입을 사용할 수 있도록 구현했습니다.

 

 

아래코는 pub가 AsyncSequence타입만 사용가능한 for-await-in구문을 사용한 코드입니다.

 

let passthroughSubject = PassthroughSubject<[Int], Never>()

passthroughSubject.sink { print($0) }

passthroughSubject.send([1,2,3,4,5,6,7,8,9,10])

✋✋✋✋✋✋
for await item in passthroughSubject.values {
    print(item)
}

///[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

 

AsyncSequence에 대해 제가 노션에 정리한 것이 있는데 아래 첨부하겠습니다.

 

 

 

 

 

번외 @Published

 

저 annotation 아마 많이들 보셨을 것이라 생각합니다.

 

 

사실 Published랩퍼는 pub였습니다...! 🤗

 

 

해당 프로퍼티 앞에 $기호를 붙여 접근할 수 있습니다.

 

 

아래코드는 랩핑된 프로퍼티에 sub를 부착하는 코드에요. 신박하지 않나요? 아님말구요🤔

 

class Weather {
    @Published var temperature: Double
    init(temperature: Double) {
        self.temperature = temperature
    }
}


let weather = Weather(temperature: 20)
✋✋✋✋✋✋✋✋
cancellable = weather.$temperature
    .sink() {
        print ("Temperature now: \($0)")
}
weather.temperature = 25

 

 

자 오늘은 여기까지 입니다.

 

 

다음글은 남은 기둥인 sub에 대한 글로 돌아오겠습니다! ✋

 

 

 

 

 

 

Publisher | Apple Developer Documentation

Declares that a type can transmit a sequence of values over time.

developer.apple.com

 

 

(Async)Sequence

Sequence

cactus-snout-d26.notion.site