22
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

こんな時どう書くの?初級者向けSwift Combineチートシート

Last updated at Posted at 2021-12-13

この記事は、ニフティグループ Advent Calendar 2021 13日目の記事です。

ニフティライフスタイル所属エンジニアから3人目の投稿です!
iOSアプリエンジニアなのでiOSアプリについての記事を投稿します。

はじめに

WWDC 2019にてCombineが発表されて早2年、ようやくiOS12のサポートが切れて自分のプロダクトにCombineが導入できる!って方もいるのではないでしょうか...
私が携わっているニフティ不動産アプリもついにiOS13以上サポートになり、今年初めてCombineをプロダクトコードに導入しました。

DelegateやClosureを駆使して実装してきた私にとって、Combineの宣言的な概念や実装を理解するのは結構大変でした。
今回は自分の復習も兼ねて、Combineをざっと学んでみたけどこんな時にどんな文法を使えば良いんだっけ?というときに使えるCombineチートシートを作成しました!

とりあえず何かをCombineを使って流してみたい

公式で用意されたPublisherを使えます。

Just

値を即時に1回だけ流すことができます。エラーは流せません。
ダミーの値を流して動作を確認したい時などに使えます。

Just("Taro")
    .map { name in
        "Hello, \(name)!"
    }
    .sink { message in
        print(message)
    }.store(in: &cancellables)
Hello, Taro!

Future

値かエラーを非同期的に1回だけ流すことができます。
クロージャで書かれているAPIをCombineで流したい時など、返却値をPublisherに変換するときに、ラップして使うことができます。
注意としてはインスタンスを生成した時点でFutureの中を実行してしまうので、Deferred で囲うことでSubscribeした時に初めて実行されるようになります。

func testPublisher() -> AnyPublisher<Int, Never> {
    Deferred {
        Future<Int, Never> { (promise) in
            // 非同期処理
            DispatchQueue.global(qos: .userInitiated).async {
                print(Thread.isMainThread)
                promise(.success(1))
            }
        }
    }.eraseToAnyPublisher()
}

testPublisher().sink { int in
    print(int)
}.store(in: &cancellables)
false
1

Empty

「Never」パブリッシャーを作成し、終了だけを流します。
Publisherを生成しなければいけないが、値もエラーも流したくないときに利用します。
私はテストコードなどでとりあえず何かを流さないといけなかったり、メソッド内が未実装のときにビルドエラーにならないように仮で実装するときに利用します。

Empty().eraseToAnyPublisher()

定期的に値を流し続けたい

Subjects

Subscriber(値を受け取る側)へ値を流すことができます。出力終了の通知を送るまで、sendを呼ぶことで継続的に値を流し続けることができます。
Subjectsには2種類あります。

CurrentValueSubject

最後に出力した値を一つ保持し、値を流すたびに保持する値も更新されます。
Subscriber(値を受け取る側)が購読を開始したときに初期値が欲しいときはこちらを利用します。

let subject = CurrentValueSubject<Int, Never>(0)
subject.sink { int in
    print(int)
}.store(in: &cancellables)
subject.send(1)
subject.send(2)
subject.send(3)
0
1
2
3

PassthroughSubject

PassthroughSubjectは値を保持せず値を流します。

let subject = PassthroughSubject<Int, Never>()
subject.sink { int in
    print(int)
}.store(in: &cancellables)
subject.send(1)
subject.send(2)
subject.send(3)
1
2
3

プロパティをPublisherにしたい

@Published

Property Wrappersという機能を使って@Publishedを先頭につけることによって、プロパティをPublisherにすることができます。

class Human {
    @Published var name: String = "Taro"
}
let human = Human()
human.$name
    .sink { name in
        print("Hello, \(name)!")
    }.store(in: &cancellables)

human.name = "Jiro"
human.name = "Saburo"
Hello, Taro!
Hello, Jiro!
Hello, Saburo!

複数のPublisherをまとめて受け取りたい

さまざまな書き方がありますが、以下の記事がとてもわかりやすくて参考になったのでリンクを紹介します。
【Swift】CombineフレームワークのCombining Operators(Publisherを結合するOperator)の動きを確認する
私は mergecombineLatestをよく使います。

まとめ

まだまだ他にもCombineの文法はたくさんありますが、私のプロダクトでよく使ったものをまとめました。
これからCombineを学ぶ人にとって参考になったら嬉しいです!

参考

22
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?