지난 시리즈에 이어서 Combine을 공부해보려고 한다.
이전 시간에는 Publisher
, Operators
, Subscriber
의 어떤 역할들을 하는지에 대해서 공부했다.
이번에는 Publisher
와 Subscriber
의 종류에 대해서 공부해보려고 한다.
지난 시간에 공부했던 것을 생각해보면 Publisher
는 시간의 흐름에 따라서 변경된 데이터를 방출하고, 종료되는 시점을 알려준다고 하였다. 이런 점에서 우리는 이미 비슷한 친구들을 보았을 것이다. 바로 NotificationCenter
, KVO
라는 친구들이다. 이 친구들은 Publisher
처럼 값이 변경될 때마다 비동기적으로 이벤트를 받았다.
하지만, 엄밀히 말하면,
NotificationCenter
는 내부적으로 Publisher 메서드를 가지고 있기 때문에Publisher
가 될 수 있다.
Publisher
는 2가지의 이벤트를 방출하게 된다.
Publisher
는 값을 지속적으로 보낼 수 있고, 값을 아예 보내지 않을 수도 있습니다. 하자만, 완료 이벤트는 단 1번만 보낼 수 있습니다. 이 완료 이벤트는 값의 방출이 종료되었다는 것을 의미하는 이벤트일 수도 있고, 에러가 발생하였음을 나타낼 수도 있습니다.
let notification = Notification.Name(rawValue: "MyNotification")
let center = NotificationCenter.default
let observer = center.addObserver(
forName: notification,
object: nil,
queue: nil
) { notification in
print("Notification received!")
}
center.post(name: notification, object: nil)
center.removeObserver(observer)
// Notification received!
위의 코드를 순차적으로 살펴 보면, Notification.Name
타입을 하나 선언해준다. 이 이름으로 발생하는 이벤트를 처리하기 위한 Observer
를 생성한다. 그 후, 해당 이름으로 NotificationCenter
에 Notification
을 발생시킨다.
이는 NotificationCenter
를 일반적으로 활용하는 방법이다. 이를 Combine을 통해서 변경해보자.
let notification = Notification.Name("MyNotification")
let center = NotificationCenter.default
let publisher = center.publisher(for: notification, object: nil)
다음 코드에서 Observer
를 생성하지 않았다. 대신해서 NotificationCenter
에서 Publisher 타입을 얻게 되었다. 포스팅을 작성하면서 코드를 완성할 것이다.
Subscriber
는 Publisher로 부터 값을 받을 수 있는 타입을 구현하는 프로토콜이다. 이는 Publisher
타입을 뜯어보면서 공부해보자. 우선은 이 정도만 알고 넘어가자.
우리가 원하는 것은 값을 전달해주는 객체와 값을 전달받은 객체를 연결시키는 것이 궁극적인 목적일 것이다. 이를 가능하게 해주는 메서드들이 존재한다.
let notification = Notification.Name("MyNotification")
let center = NotificationCenter.default
let publisher = center.publisher(for: notification, object: nil)
let subscription = publisher
.sink { _ in
print("Notification received from a publisher !")
}
center.post(name: notification, object: nil)
subscription.cancel()
// Notification received from a publisher !
위에서 보았던 코드에서 Publisher
에 sink
메서드를 통해서 값이 들어왔을 때 어떤 대응을 할 것인지에 대해서 설정하고, 값을 전달하였다. 우리는 sink
메서드를 통해서 Subscribing
에 성공했다. sink
메서드의 이름이 가라앉는 느낌을 굉장히 많이 준다. 하지만, 이는 다른 의미에서 사용되지만, 한글로 명확한 이름이 생각나지는 않는다. sink
로 쓰는 것이 가장 찰떡이다. ㅋㅋㅋ
아까 위에서 Publisher
는 한번의 완료 이벤트를 보낸다고 하였다. 이는 어떻게 이벤트로 받을 수 있을까? 단순히 sink(receiveCompletion, receiveValue)
메서드를 사용하면 된다. 그러면, 완료 completion을 핸들링할 수 있게 된다.
마지막으로 값을 저장하기 위해서 sink
메서드를 통해서 클로져 안에서 활용해야 하는 것인가? 아니다. KVO
를 통해서 값을 직접적으로 저장할 수 있다.
class SomeObject {
var value: String = "" {
didSet {
print(value)
}
}
}
let object = SomeObject()
let publisher = ["Hello", "world!"].publisher
_ = publisher
.assign(to: \.value, on: object)
assign
메서드를 통해서 값을 직접적으로 할당할 수 있다.