Combineを利用した複数の通信の束ね方

#はじめに
「Qiitaに記事を投稿してみたい!」と思っていたものの、中々書くことが出来ていなかったのですが、今回、自分のメモもかねて最近興味のある**Combine**について記事を書いてみようと思います！
初の投稿なので、暖かい目で読んで頂けると嬉しいです！
#目次
1.Combineとは
2.本題
3.最後に
#1.Combineとは
まず**Combine**についてですが、
以下の記事がとても参考になると思います。

https://medium.com/@shiba1014/swiftのcombineを知る-fad8e3d902fe

要は**Publisher**と**Operator**と**Subscriber**というのがあり、それぞれざっくり
Publisher:データの発行
Operator:データの加工
Subscriber:データの購読
という役割があるという理解でいいと思います。

自分はこの「発行」と「購読」という表現に少し違和感があり、理解に苦しみました。
RxSwiftの記事ではありますが、以下の記事がイメージの参考になると思います！

https://codezine.jp/article/detail/11051

要は、先人達が自分たちの中で馴染みのある表現に置き換えようと思った時、この「発行」「購読」というやや古い表現になり、それが現在まで引き継がれてしまったよ、というだけの話ですね。
自分の中で腑に落ちる表現があったらそっちでイメージしてもらっても大丈夫だと思います！


#2.本題
で、本題となる通信の束ね方についてですが、
先にコードをご覧頂きましょう。

```swift
import Combine
import Foundation

//適当なモデルを用意
struct Model: Codable {
    let publishingOffice: String
}

var subscriptions = Set<AnyCancellable>()

func request() {
    let urlOfTokyo = URL(string: "https://www.jma.go.jp/bosai/forecast/data/forecast/130000.json")!
    let publisherOfTokyo = URLSession.shared.dataTaskPublisher(for: URLRequest(url: urlOfTokyo))
        .map({(data, response) in return data})
        .decode(type: [Model].self, decoder: JSONDecoder())
        .eraseToAnyPublisher()

    let urlOfOsaka = URL(string: "https://www.jma.go.jp/bosai/forecast/data/forecast/270000.json")!
    let publisherOfOsaka = URLSession.shared.dataTaskPublisher(for: URLRequest(url: urlOfOsaka))
        .map({(data, response) in return data})
        .decode(type: [Model].self, decoder: JSONDecoder())
        .eraseToAnyPublisher()

    Publishers.Zip(publisherOfTokyo, publisherOfOsaka)
        .sink(
            receiveCompletion: {completion in
                switch completion {
                case .finished:
                    print("通信成功")
                case .failure:
                    print("通信失敗")
                }},
            receiveValue: { tokyo, osaka in
                //あとは煮るなり焼くなり
            }
        )
        .store(in: &subscriptions)
}
 ```


これで理解できる人はこの先新しい情報はないと思いますので閉じちゃって大丈夫です！

では1行ずつ見ていきたいと思います。

```swift
let urlOfTokyo = URL(string: "https://www.jma.go.jp/bosai/forecast/data/forecast/130000.json")!
```
気象庁の天気予報をJSONで返してくれるURLです。最近少し？Twitterでざわついてたやつですね。
気象庁はサービスの継続性を約束はしていないので、途中で使えなくなるかもしれませんが、政府の利用規約範囲内で使用させて頂きましょう。

```swift
let publisherOfTokyo = URLSession.shared.dataTaskPublisher(for: URLRequest(url: urlOfTokyo))
```
Publisherにはいくつか種類がありますが、今回はdataTaskPublisherというURLSessionに予め用意されたPublisherを利用します。
これにより、通信を行うと共に、Publisherとなってデータの発行も行っています。

```swift
.map({(data, res) in return data})
```
ここでは、先ほどPublisherから発行された値をmapを使ってData型のものだけを取り出しています。
データの加工を行うこの部分がOperatorというやつです。

```swift
.decode(type: [Model].self, decoder: JSONDecoder())
```

デコードしています。

```swift
.eraseToAnyPublisher()
```

これは自分もあまり理解していませんが、要は型をAnyPublisher型に統一し、扱い易くしているのかな？という感じで理解をしています。
ちゃんと理解出来たら、加筆したいと思います。

ここまで、Publisherから発行されたデータをOperatorによって加工していますが、全体として行っていることは結局データの発行なので、大きくPublisherと考えることが出来ます。

このPublisherを購読することでSubscriberとなります。

今回は複数の通信を束ねるということだったので、２つのPublisherを購読します。

もう1つのPublisherを用意しましたが、全く同じコードなので説明は割愛します。

では続きを見ていきます。

```swift
Publishers.Zip(publisherOfTokyo, publisherOfOsaka)
```
Publisherから発行される複数のデータをまとめて処理する方法は、以下の記事にある通り、

https://qiita.com/shiz/items/cff44fe801243b8a5496

様々なものがありますが、その中でも今回は記事の中でもある通り、
>結合している全てのPublisherから新しい値が出力されると値が出力される

**Zip**というものを使っています。

これにより2つのPublisherを束ねています。

そして以下で購読します。

```swift
.sink(
    receiveCompletion: {completion in
        switch completion {
        case .finished:
            print("通信成功")
        case .failure:
            print("通信失敗")
        }},
    receiveValue: { tokyo, osaka in
        //あとは煮るなり焼くなり
    }
)
```

購読する方法もいくつかありますが、今回は.sinkを使ってZipでまとめた２つのPublisherを購読しています。その他の購読方法としては、以下の記事が参考になると思います。

https://qiita.com/shiz/items/5efac86479db77a52ccc

最後に

```swift
.store(in: &subscriptions)
```

でsinkで購読していた処理をキャンセルしています。
最初の「発行」「購読」の話で言うと、「購読を止める」というイメージです。
以下の記事を参考にしました。

https://tech.dely.jp/entry/2019/12/11/103000


以上、最近気になっているCombineにおける複数の通信の束ね方の紹介でした。

#3.最後に
記事を1本書くだけでもとても疲れる作業だったので、先人の人たちには大感謝です。。。
今後、自分も皆さんに自分が身に付けた知識を還元出来るように気になったことを積極的にアウトプットとして残していきたいと思います！
記事の中でのご指摘等ございましたら、コメント頂けると嬉しいです！
最後まで読んでいただきまして、ありがとうございました！