이번 글에선 Subject 에 대해 알아보도록 하겠습니다.
공식 문서에도 보이듯 Subject 는
Publisher 프로토콜을 따르는 것을 확인할 수 있습니다.
설명을 살펴보면
Subject 는 스트림에 send(_:) 메서드를 호출해서
값을 주입(inject) 할 수 있는 publisher 라고 작성되어 있습니다.
Subject 내부는 어떻게 만들어져 있는지 살펴보면
3가지의 send 메서드가 있는것을 확인할 수 있습니다.
중요한건 value, completion, subscription 을
subscriber 에게 전달하는 것을 확인할 수 있습니다.
그렇다면 subject 라는 것도 프로토콜이니
누군가 채택을 해서 사용할텐데
이를 채택해서 사용하는 것은
CurrentValueSubject, PassthroughSubect
라는 것이 있습니다.
순서대로 같이 살펴보도록 하겠습니다.
1. CurrentValueSubject
CurrentValueSubject 는 현재 값을 가지며, 값을 업데이트하면
현재 값을 subscriber 에게 즉시 전달하는 특징을 갖고 있습니다.
위의 메서드와 같이 CurrentValueSubject 를 사용할땐
반드시 value 를 설정해줘야 합니다.
CurrentValueSubject 예제를 살펴보면
// CurrentValueSubject 예제
let currentValueSubject = CurrentValueSubject<String, Never>("Initial Value")
let currentValueSubscriber1 = currentValueSubject.sink { value in
print("Subscriber 1 received value: \(value)")
}
currentValueSubject.send("🍁")
let currentValueSubscriber2 = currentValueSubject.sink { value in
print("Subscriber 2 received value: \(value)")
}
currentValueSubject.send("🌕")
value 를 "Initial Value" 로 설정해주고 subscriber1, subscriber2 를 두고
send 하는 코드를 작성해봤습니다.
subscriber1 이 처음 호출될때 Initial Value 가 제일 먼저
호출되는 것을 확인할 수 있고
마지막에 🌕 을 send 할때 subscriber1, subscriber2
두개의 값이 모두 업데이트 된 것을 확인할 수 있습니다.
(추가적으로 호출의 순서가 보장되지 않는다는 것 또한 확인할 수 있습니다.)
2. PassthroughSubject
currentValueSubject 와는 다르게
init 메서드 사용시 파라미터가 필요하지 않은걸 확인할 수 있습니다.
코드를 통해 살펴보면
// PassthroughSubject 예제
let passthroughSubject = PassthroughSubject<String, Never>()
let passthroughSubscriber1 = passthroughSubject.sink { value in
print("Subscriber 1 received value: \(value)")
}
passthroughSubject.send("🍡")
passthroughSubject.send("🌾")
let passthroughSubscriber2 = passthroughSubject.sink { value in
print("Subscriber 2 received value: \(value)")
}
passthroughSubject.send("🥮")
PassthroughSubject 또한
마지막에 🥮 을 send 할때 subscriber1, subscriber2
두개의 값이 모두 업데이트 된 것을 확인할 수 있습니다.
그렇다면 지금까지 알아본 것을 어떻게 활용할 수 있을지
알아보도록 하겠습니다.
// Product 모델 정의
struct Product {
let name: String
}
// 초기 제품 목록
let initialProducts = [
Product(name: "iPhone14"),
Product(name: "AirPods2"),
Product(name: "MacBookPro"),
Product(name: "MacBookAir")
]
// CurrentValueSubject를 사용하여 제품 목록을 저장하고 공유합니다.
var productSubject = CurrentValueSubject<[Product], Never>(initialProducts)
// 현재 제품 목록을 출력
print("🍎 현재 Apple 제품 목록")
print(productSubject.value.map { $0.name })
// 새로운 제품을 추가하여 제품 목록을 업데이트합니다.
let newProduct = Product(name: "iPhone15ProMax")
productSubject.send(productSubject.value + [newProduct])
// 업데이트된 제품 목록을 출력
print("🍏 업데이트된 제품 목록")
print(productSubject.value.map { $0.name })
초기 제품 목록이 있기에 Subject 를 만들어줄때
CurrentValueSubject 를 사용했고,
제품의 이름에 접근하기 위해 value 를 사용했습니다.
value 로만 접근하게 된다면
이런 결과값이 출력되기 때문에 map 연산자를 사용하여
아래와 같이 name 만 가져올 수 있게 하였습니다.
이렇게 현제 제품 목록을 확인할 수 있고
productSubject 에 newProduct 를 추가하여 send 하게 된다면
prodcutSubject 값이 업데이트 되어 출력된 것을 확인할 수 있습니다!
다음 글에선 작업 중인 항목을 취소할 수 있는
Cancellable 에 대해 알아보도록 하겠습니다.
읽어주셔서 감사합니다! 😊
'Combine' 카테고리의 다른 글
[Combine] ViewModel 의 상태변화를 View 에게 알려주고 싶다면 어떻게 해야할까? (0) | 2023.10.25 |
---|---|
[Combine] SwiftUI 에서는 Publisher 를 어떻게 구독할까? (0) | 2023.10.08 |
[Combine] Publisher 와 Subscriber 는 어떻게 연결할 수 있을까? (0) | 2023.09.28 |
[Combine] Publisher / Operator / Subscriber 알아보기 (0) | 2023.09.27 |
[Combine] eraseToAnyPublisher 는 무엇이고 어떻게 사용하는걸까? (0) | 2023.09.23 |