내용
- Publisher -> Operator -> Subscriber
- Subscription [ Subscriber(구독자)가 Publisher(크리에이터)에 연결됨을 나타낸다.]
- @Published
Combine - 놈놈놈
- - Publisher [발행하는 놈 (크리에이터)]
- - Subscriber [구독하는 놈]
- - Operator [변경하는 놈]
이벤트 발생 이벤트 가공 이벤트 소비
Publisher -> Operator -> Subscriber
- data를 그냥 보낼 수 있고
- data를 알고 있는 상태로 보낼 수도 있고
- data를 변형해서 보낼 수도 있다.
Publisher 크리에이터 즉, 데이터를 배출하는 놈
- 구체적인 output 및 실패 타입을 정의
- Subscriber(구독자) 이 요청한 것만큼 데이터 제공
여기 까지 보면 Publisher 이놈으로 뭘 정의하고 Subscriber 이놈으로 뭔가를 처리하겠네? 라는 생각이 든다.
Operator는 data를 변형할때 쓰나? 무튼,
Publisher [발행자(크리에이터)]
protocol Publisher {
associatedtype Output
associatedtype Failure : Error
func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
}
Subscriber [구독자]
public protocol Subscriber : CustomCombineIdentifierConvertible {
associatedtype Input
associatedtype Failure : Error
func receive(subscription: Subscription)
func receive(_ input: Self.Input) -> Subscribers.Demand
func receive(completion: Subscribers.Completion<Self.Failure>)
}
위 코드를 보니 동일한 Output, Input Type를 가져야하고 동일한 Failuer Type을 가져야 하나보다.
// Publisher 변수
associatedtype Output
associatedtype Failure : Error
// Subscriber 변수
associatedtype Input
associatedtype Failure : Error
빌트인 Publisher인 Just, Future가 있다.
just는 값을 다루고, Future는 Funtion을 다룬다.
// Publisher & Subscriber
// Just는 한번 전송하고 끝
let just = Just(1000)
let subscription1 = just.sink { value in
print("Receuved Value: \(value)")
}
iOS에서 자동으로 제공해 주는 것들도 있다.
- NotificationCenter
- Timer
- URLSession.dataTask
Subscriber(구독자)는 Publisher(발행자)에 데이터를 요청하고 Input, Failure 타입을 정의한다.
- Publisher을 구독후, 필요한 데이터를 개수와 함께 요청
- 파이프라인을 취소할 수 있음.
- 빌트인 Subscripber인 assign과 sink가 있다.
- sink 는 Publisher가 제공한 데이터를 받을수 있는 클로져를 제공함
- assign 는 Publisher가 제공한 데이터를 특정 객체의 *키패스(KeyPath)에 할당
// sink
let relay = PassthroughSubject<String, Never>()
let subscription1 = relay.sink { value in
print("subscription1 received value \(value)")
}
let variable = CurrentValueSubject<String, Never>("")
variable.send("Initial text")
let subscription2 = variable.sink { value in
print("supscription2 received value \(value)")
}
variable.send("More text")
variable.value
/*
supscription2 received value Initial text
supscription2 received value More text
*/
let publisher = ["Here", "we", "go"].publisher // 크리에이터
publisher.subscribe(relay) // relay를 publisher에 구독
/*
subscription1 received value Here
subscription1 received value we
subscription1 received value go
*/
publisher.subscribe(variable) // relay를 publisher에 구독
/*
supscription2 received value Here
supscription2 received value we
supscription2 received value go
*/
let arrayPublisher = [1, 3, 5, 7, 9].publisher
let subscription2 = arrayPublisher.sink { value in
print("Receuved Value: \(value)")
}
/*
Receuved Value: 1
Receuved Value: 3
Receuved Value: 5
Receuved Value: 7
Receuved Value: 9
*/
class MyClass {
var property: Int = 0 {
didSet {
print("Did set property to \(property)")
}
}
}
/*
Did set property to 1
Did set property to 3
Did set property to 5
Did set property to 7
Did set property to 9
*/
// assign
let object = MyClass()
let subscription3 = arrayPublisher.assign(to: \.property, on: object)
print("Final Value\(object.property)") // Final Value 9
//object.property = 3 // Did set property to 3
*키패스(KeyPath): 키패스를 사용하면 프로퍼티의 참조를 저장할 수 있다.
즉, 그렇게 만들어진 keyPath를 subscript[keyPath:]에 매개변수로 전달하면 그 프로퍼티에 접근할 수 있다.
Subscription [ Subscriber(구독자)가 Publisher(크리에이터)에 연결됨을 나타낸다.]
A protocol representing the connection of a subscriber to a publisher.
: 게시자와 구독자의 연결을 나타내는 프로토콜입니다.
protocol Subscription : Cancellable, CustomCombineIdentifierConvertible
- Publisher이 발행한 구독 티켓을 가지고 있다고 생각하면 된다.
- 티켓을 가지고 있으면 데이터를 받을수도 있지만 이 티켓을 끊으면 구독 관계도 사라진다.
- Cancellable protocol을 다르고있기 때문에 Subscription을 통해 연결을 Cancel 할 수 있다.
Sbject (Publisher를 채택한 프로토콜)
A publisher that exposes a method for outside callers to publish elements.
: 외부 호출자가 요소를 게시할 수 있는 메서드를 노출하는 게시자입니다.
protocol Subject<Output, Failure> : AnyObject, Publisher
- send(_:) 메소드를 이용해 이벤트 값을 주입시킬수 있는 Publisher
- 기존의 비동기 처리 방식에서 combine 전환시 유리
- 2가지 빌트인 타입이 있다.
- PassthroughSubject
- Subcriber가 달라고 요청하면, 그때 부터, 받은 값을 전달해주기만 한다.
- 전달한 값을 들고 있지 않다.
- CurrentValueSubject
- Subcriber가 달라고 요청하면, 최근에 가지고 있던 값을 전달하고, 그때 부터, 받은 값을 전달 한다.
- 전달한 값을 들고 있다.
- PassthroughSubject
Ex] PassthroughSubject
// Publisher
let subject = PassthroughSubject<String, Never>()
let subscription = subject
.print("[Debug]")
.sink { value in
print("Subscriber received value: \(value)")
}
subject.send("Hello")
subject.send("Hello amigo!")
subject.send("Hello for the last time!")
//subject.send(completion: .finished)
subscription.cancel() // subscription도 cancel함수가 있어 관계를 끊을 수 있다.
subject.send("가나?")
Ex] CurrentValueSubject
let currentValueSubject = CurrentValueSubject<String, Never>("manu")
let subscriber = currentValueSubject.sink(receiveValue: {
print($0)
})
currentValueSubject.value = "안녕"
currentValueSubject.send("하이")
@Published (Publisher) [발행자로 만들어주는 어노테이션]
- @Published 로 선언된 프로퍼티를 퍼블리셔로 만들어준다.
- 클레스에 한해서 사용됨 (구조체에서 사용이안된다. why? 구조체는 값타입이기때문에.)
- $를 이용해 퍼블리셔에 접슨할 수 있다.
final class SomViewModel {
@Published var name: String = "minuco"
var age: Int = 20
}
final class Label {
var text: String = ""
}
let label = Label()
let vm = SomViewModel()
print("text: \(label.text)")
// $로 name을 Subscriber(구독) 해서 assign을 통해 lable객체의 text에 "ninuco" 전달.
vm.$name.assign(to: \.text , on: label)
print("text: \(label.text)")
vm.name = "Jeny"
vm.age = 30 // 아무일도 일어나지 않음.
// why?? 위와 비교해보면 알게됨.
print("text: \(label.text)")
'iOS > TIL' 카테고리의 다른 글
[TIL] MVVM(3) (0) | 2023.12.05 |
---|---|
[TIL] MVVM(2) (0) | 2023.11.28 |
[TIL] MVVM(1) (2) | 2023.11.25 |
[TIL] NaverMapAPI (0) | 2023.03.16 |