[Combine] eraseToAnyPublisher()の使い時

TL;DR

Use eraseToAnyPublisher() to expose an instance of AnyPublisher to the downstream subscriber, rather than this publisher’s actual type.

Example

Publisherを用意するためにSubject(Publisherの一種)を使うことがあると思う。

struct NewsAgency {
    let subject = PassthroughSubject<Headline, Never>()
}

しかし、Subjectを公開しておくと、外部から値をinjectできてしまう(むしろ本来の用途)。
例えば、この例だと不正なニュースを流せてしまう。

let agency = NewsAgency()
agency.subject.send(Headline(title: "不正なニュース")

ニュースの送信は、NewsAgency内部でのみ行いたい。
そこでSubjectprivateにし、AnyPublisher<Headline, Never>だけを公開する。

struct NewsAgency {
    private let subject = PassthroughSubject<Headline, Never>()
    var publisher: AnyPublisher<Headline, Never> {
        return subject.eraseToAnyPublisher()
    }

    private func gatherNewsFromAllOverTheWorld() {
      // ニュースの送信は内部でのみ実行可能
      subject.send(Headline(title: "【速報】日本のGDPが200%成長"))
      subject.send(Headline(title: "【速報】シリアの内戦が終結"))
    }
}

これにより、外部からsendが使えなくなり、subscribeしかできなくなった。

let agency = NewsAgency()
self.subscription = agency
    .publisher
    .sink(receiveValue: { headline in
        print("You've got a new headline: \(headline)")
    })

ほかにも、Publisherをオペレーターで操作したときにPublishersネームスペースで定義された型を返すが、これを晒したくないときに使える。

以上