Swift Combine初めの1歩

# 概要

Swift初心者でCombineを少し触れたい人へ。
簡単なコードを説明します。


# 今日の例

今日はこれがわかるようになるのが目標！
わからなくてもOK。後で説明します。
わかった人は読まなくて良いです。

```swift
import Combine

let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)

let publisher = b.combineLatest(a).map{b,a in a + b}

publisher.sink{ added in
    print(added)
}
a.send(10)

```

まじ意味不明だと思いますが、標準出力(print)には何が表示されそうでしょうか？

答えは

```
4
11
```

2行表示されます。
最初の`4`は`3 + 1`
次の11は`10 + 1`です。

# とりあえずimport

Combineのimportは

```swift

import Combine
```

でできます。**iOS 13**以上で使用可能。

Playgroundなどで簡単に試せます。

# Combineの用語

Combineでとりあえず覚えるべき用語にPublisherとSubscriberがある。

|名前|日本読み|何してる|
|---|---|---|
|Publisher|パブリッシャー|データを発行|
|Subscriber|サブスクライバー|データを購読|

わからなくてもいいからとりあえず2単語暗記!
Publisher, Subscriber


# とりあえずJust
Justは(多分)一番簡単なPublisherです。

とりあえず定義を見にいってみます。(一部抜粋)

```swift
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
public struct Just<Output> : Publisher {
   // ... (省略)
}

```

とりあえずPublisherを継承しているしPublisherですね。
iOS 13.0以上でしか使えないのも事実らしい。

このJustの説明には何が書かれているかというと、
> A publisher that emits an output to each subscriber just once, and then finishes.

*１つのアウトプットを各々の**Subscriber**に対して、**ただ(just)**1回出力する**Publisher**で、その後finishします。*

この文章からは以下がわかります。

* Justは一回だけ何かを出力する。
* JustはPublisher
* Subscriberは複数いる可能性がある
* 1回出したら終了(finish)する


`値を１つ出すpublisher`だと無理やり覚えてみる。

# sinkについて

例に出したコードで

```swift

publisher.sink{ added in
    print(added)
}
```

というのがありました。

この[sink](https://developer.apple.com/documentation/combine/publisher/sink(receivevalue:))の説明を見てみると、

> Attaches a subscriber with closure-based behavior to a publisher that never fails.

*クロージャベースの振る舞いを持つSubscriberを失敗しないpublisherにアタッチします。*

意味は不明ですが、subscriberをアタッチしてくれそうなので使ってみましょう。

## 例1

先ほどのJustでsinkを呼び出すとどうなるでしょうか？

```swift
Just(1).sink { r in
    print(r)
}
```

これは`１`が表示されます。


publisher(Just)は1を一回だけ出力して、sinkすることでrにそれが入ってくるので`1`が表示されたようです。

## 例2

まだ説明していない`CurrentValueSubject<Int, Never>(3)`についても試してみましょう。

```swift
CurrentValueSubject<Int, Never>(3).sink { r in
    print(r)
}
```

これは`3`が出力されます！

一体Justと何が違うのか、、、

# CurrentValueSubjectについて

CurrentValueSubjectとは何でしょうか？
定義をみてみます。

```swift

@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
final public class CurrentValueSubject<Output, Failure> : Subject

```

こいつは**Subject**らしい。Publisherじゃなかった!
それとiOS 13.0以上でしか使えないのは`Just`と一緒ですね。

[説明](https://developer.apple.com/documentation/combine/currentvaluesubject)には、

> A subject that wraps a single value and publishes a new element whenever the value changes.

*１つの値をラップして、値が変わった時の新しい値を**Publish**する**Subject**。*とあります。

PublishができるのはPublisherだけなのでは？
Subjectの定義も見に行きます。

```swift
public protocol Subject : AnyObject, Publisher
```

どうやらSubjectは**Publisherの1種**みたいですね。

Justと比べてみると、

|||
|---|---|
|Just|1つのアウトプットを**ただ(just)**1回出力する**Publisher**|
|CurrentValueSubject|１つの値をラップして、値が変わった時の新しい値を**Publish**する**Subject**|


Justとの違いは値が変わった時にその新しい値をpublishしてくれるということ！
試してみましょう。

```swift

import Combine

let a = CurrentValueSubject<Int, Never>(3)

a.sink{ added in
    print(added)
}

a.value = 100
a.value = 10000
a.send(9999)
```

何が出力されそうでしょうか？
答えは

```
3
100
10000
9999
```

a.valueの変更のたびにprintしています。
またa.send()でも、値の変更を表現できるようです。


# ラスト1行!

最初の例の大部分が説明できてきました。

```swift
import Combine

let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)

let publisher = b.combineLatest(a).map{b,a in a + b}

publisher.sink{ added in
    print(added)
}
a.send(10)

```

後、`let publisher = b.combineLatest(a).map{b,a in a + b}`が分かれば、なんとなくコードの意味がわかりそうです。

# combineLatest

まず`b.combineLatest(a)`に注目しましょう。

これはどういう意味でしょうか？

```swift

let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)
```
だったので、aもbもどちらもPublisherですね。

[combineLatest](https://developer.apple.com/documentation/combine/just/combinelatest(_:))の説明をみてみましょう。

> Subscribes to an additional publisher and publishes a tuple upon receiving output from either publisher.

*追加されたPublisherを購読(Subscribe)して、各々のPublisherから受け取った出力のタプルをPublishする。*

こいつはSubscribeもPublishもしているようです。

## 実際に使う

動かしてみる。

```swift
import Combine

let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)

let publisher = b.combineLatest(a).sink{r in
    print(r)
}
```

これだと何が出そうですか？

正解は

```
(1, 3)
```

そういえばcombineLatestはタプルを返すと先ほどいっていました。

aの値を変えてみましょう。3行コードを追加します。

```swift
import Combine

let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)

let publisher = b.combineLatest(a).sink{r in
    print(r)
}

a.value = 4
a.value = 5
a.send(6)

```

どうなりそうですか？

正解は

```
(1, 3)
(1, 4)
(1, 5)
(1, 6)
```

bの値は変わらないままaの値の変更によってprintされています。

動きはわかりましたでしょうか？


## SubscribeもPublishもしているcombineLatestとは何者か？

定義をみてみると、

```swift

    public func combineLatest<P>(_ other: P) -> Publishers.CombineLatest<Self, P> where P : Publisher, Self.Failure == P.Failure

```

長くて意味は不明ですが、返しているのは、`Publishers.CombineLatest<Self, P>`。

`Publishers.CombineLatest`は

```swift

public struct CombineLatest<A, B> : Publisher
```

で**Publisher**です。

なので、`b.combineLatest(a)`は2つのPublisherを1つのPublisherにするような関数です。



# map

mapは

> Transforms all elements from the upstream publisher with a provided closure.

*上流のPublisherから来る全ての要素を与えられたクロージャーを使って変形する。*

全部変形するらしい。

定義をみてみると

```swift
public func map<T>(_ transform: @escaping (Self.Output) -> T) -> Publishers.Map<Self, T>
```

結局こいつもPublisherを返す！

## 例1

先ほど`combineLatest`で出した例。

```swift
import Combine

let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)

let publisher = b.combineLatest(a).sink{r in
    print(r)
}

a.value = 4
a.value = 5
a.send(6)

```

結果は、

```
(1, 3)
(1, 4)
(1, 5)
(1, 6)
```

でしたね。

このコードのcombineLatestの後ろにmapをつけてみます。

```swift

import Combine

let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)

let publisher = b.combineLatest(a)
    .map{(b,a) in a+b}
    .sink{r in print(r)}

a.value = 4
a.value = 5
a.send(6)
```

何が返りそうでしょうか？

正解は、

```
4
5
6
7
```

これは

```
(1, 3)
(1, 4)
(1, 5)
(1, 6)
```

この結果の各々2つの値を足していることがわかります。

## 例2

`.map{(b,a) in a+b}`を`.map{(b,a) in a}`に変えてみました。
出力は何になりますか？

```swift

import Combine

let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)

let publisher = b.combineLatest(a)
    .map{(b,a) in a}
    .sink{r in print(r)}

a.value = 4
a.value = 5
a.send(6)
```

正解は

```
3
4
5
6
```

# まとめ

|||
|---|---|
|Combineを使うには|iOS 13.0以上で`import Combine`|
|Justとは|値を1回だけ出力する**Publisher**|
|sinkとは|Publisherの出力した値を購読(Subscribe)して処理できる|
|CurrentValueSubject|値を変化でき、変化した値を出力する**Publisher**(Subject)|
|CurrentValueSubjectの値を変更するには？| CurrentValueSubject.valueに代入するか、CurrentValueSubject.send(value)に値を入力|
|combineLatestとは|2つのpublisherを１つのpublisherにして、値をタプルで返す。|
|mapとは|publisherからきた値を全部変形する|


目標の以下のコードの動きは理解できましたか？


```swift
import Combine

let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)

let publisher = b.combineLatest(a).map{b,a in a + b}

publisher.sink{ added in
    print(added)
}
a.send(10)

```