Combine 을 배워보자(2)

미니·2023년 3월 6일
0

Combine

목록 보기
1/2
post-thumbnail

지난 시리즈에 이어서 Combine을 공부해보려고 한다.
이전 시간에는 Publisher, Operators, Subscriber의 어떤 역할들을 하는지에 대해서 공부했다.

이번에는 PublisherSubscriber의 종류에 대해서 공부해보려고 한다.

Publisher

지난 시간에 공부했던 것을 생각해보면 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를 생성한다. 그 후, 해당 이름으로 NotificationCenterNotification을 발생시킨다.

이는 NotificationCenter를 일반적으로 활용하는 방법이다. 이를 Combine을 통해서 변경해보자.

let notification = Notification.Name("MyNotification")
let center = NotificationCenter.default
    
let publisher = center.publisher(for: notification, object: nil)

다음 코드에서 Observer를 생성하지 않았다. 대신해서 NotificationCenter에서 Publisher 타입을 얻게 되었다. 포스팅을 작성하면서 코드를 완성할 것이다.

Subscriber

Subscriber는 Publisher로 부터 값을 받을 수 있는 타입을 구현하는 프로토콜이다. 이는 Publisher 타입을 뜯어보면서 공부해보자. 우선은 이 정도만 알고 넘어가자.

Subscribing

우리가 원하는 것은 값을 전달해주는 객체와 값을 전달받은 객체를 연결시키는 것이 궁극적인 목적일 것이다. 이를 가능하게 해주는 메서드들이 존재한다.

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 !

sink(receiveValue:)

위에서 보았던 코드에서 Publishersink메서드를 통해서 값이 들어왔을 때 어떤 대응을 할 것인지에 대해서 설정하고, 값을 전달하였다. 우리는 sink 메서드를 통해서 Subscribing에 성공했다. sink 메서드의 이름이 가라앉는 느낌을 굉장히 많이 준다. 하지만, 이는 다른 의미에서 사용되지만, 한글로 명확한 이름이 생각나지는 않는다. sink로 쓰는 것이 가장 찰떡이다. ㅋㅋㅋ

sink(receiveCompletion:, receiveValue:)

아까 위에서 Publisher는 한번의 완료 이벤트를 보낸다고 하였다. 이는 어떻게 이벤트로 받을 수 있을까? 단순히 sink(receiveCompletion, receiveValue) 메서드를 사용하면 된다. 그러면, 완료 completion을 핸들링할 수 있게 된다.

assign(to:), assign(to:, on:)

마지막으로 값을 저장하기 위해서 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 메서드를 통해서 값을 직접적으로 할당할 수 있다.

0개의 댓글