【Swift】 Combineを使用するメリットについて考えてみる

iOS, Swift

はじめに

こんにちは。iOSアプリ開発グループの神山です。

最近Combineについて触れる機会があり、絶賛勉強中です。

概要や使い方についての記事はたくさんあったのですが、そもそもCombineを使用するメリットやどのような恩恵を受けられるのかに焦点を当てた記事は少なかったので自分なりに考えてまとめてみました。

Combineとは

Combineとはある特定のイベントに対して、イベントの発行と購読をすることができるフレームワークです。

Appleのドキュメントにはこのような記載があります。

Customize handling of asynchronous events by combining event-processing operators.

The Combine framework provides a declarative Swift API for processing values over time. These values can represent many kinds of asynchronous events. Combine declares publishers to expose values that can change over time, and subscribers to receive those values from the publishers.

つまり、Combineフレームワークは時間の経過で値を処理するための宣言的なAPIを提供し、これらの値を使用して多くの種類の非同期イベントを表すことのできるフレームワークと言えます。

※ 「宣言的」についてはこちらの記事がとても分かりやすかったです。宣言的UIとしてSwiftUIが登場したことも踏まえると、Swiftで今後開発する上で「宣言的」を理解しておくことは大切だと思います。

メリット

次にCombineを採用することのメリットについて見ていきましょう。

ここではメリットについて2点挙げてみます。

1. ネストしたクロージャやコールバックの解消

Combineに関するAppleのドキュメントを読み進めていきますと以下のような記載がありました。

By adopting Combine, you’ll make your code easier to read and maintain, by centralizing your event-processing code and eliminating troublesome techniques like nested closures and convention-based callbacks.

「ネストしたクロージャや規約ベースのコールバックなどの面倒な処理を排除し、コードを読みやすくかつ保守しやすくすることができます」とのことでした。

言葉だけだとイメージが掴みにくいので、実際にネストしたクロージャでの処理がCombineを使うとどのようになるのか実装してみましょう。

ネストしたクロージャ

実際にネストしたクロージャとなるのは、API通信を伴う処理を複数組み合わせた場合などがあるかと思います。例えば、API通信をした際に受け取った結果を使ってさらにAPI通信を行なったり、受け取った結果からダウンロードの処理をした時などです。

今回は確認しやすくするため、簡易的にネストしたクロージャを作成してみます。

このようにネストされたクロージャーはコードとしては読みにくい部分があります。

次はこれをCombineで置き換えてみましょう。

Combineではクロージャを使用せずに処理をすることができるため、ネストもなくなりコードも見やすくなったことがわかるかと思います。

※ これらのネストしたクロージャの排除はasync/awaitでも置き換えることは可能です。

2. 複数なイベントに対する管理の簡略化

Combineには流れてきたイベントを加工して、新たなイベントを再発行して流すことのできる Operatorという機能を有しています。

このOperatorを使うことで、流れてきたイベントを受け取ってから値を処理するのではなく、イベントに対して事前に値の処理などを行なってから受け取ることができます。

例として、会員登録する際のケースを見ていきましょう。会員登録ではユーザーが入力したメールアドレスやパスワードが問題ないかどうか、不正な値が入力された際にエラーメッセージを表示するかどうか、登録のボタンをメールアドレスとパスワードが有効な時のみ押下できるようにするかどうかなど様々なイベントが入り交わります。

このような場合にOperatorを組み合わせることでシンプルに処理をまとめられます。

ここではOperatorのイベントとして用意されている mapcombineLatestを使用し値を変換しました。

これにより、メールアドレスやパスワードに問題がないかや不正な値が入力された際にエラーメッセージを表示するかどうかは emailValidated, passwordValidated、メールアドレスやパスワードに有効な値が入力された場合にのみ登録ボタンを押下できるようにするかどうかは isEnabledを購読するという処理を作成することができました。

※ これらの機能は RxSwiftReactiveSwiftといった関数型リアクティブプログラミングのライブラリでも置き換えることができ、Combine ≒ RxSwift、Combine ≒ ReactiveSwiftとも言えます。ただ、CombineはAppleが提供している純正のフレームワークであるため、今後の開発においてはRxSwiftやReactiveSwiftよりCombineが主軸になるかと思います。

Combineの使い所

Combineのメリットについて確認してきましたが、ネストしたクロージャの解消以外に実際の使い所について自分なりに考えてみました。

変化していく状態の管理

Combineは時間の経過で値を処理するためのフレームワークとあるように、イベントを一元化することができ、受け取った値によって状態を変えるといった状態管理をしやすい一面があります。

それぞれの状態をenumで定義しそれらの変化をViewが監視するようにすれば、通信中、通信後成功、通信後失敗といった状態によってViewの切り替えを行うといった処理がしやすくなります。

このように standby loading doneといった状態の流れに沿ってViewを置き換えることができました。

loading done
Loading.png Done.png

さいごに

今回はCombineを使用するメリットについて考えてみました。

CombineはAppleのドキュメントに記載のあったネストしたクロージャやコールバックを解消する以外にもさまざまな目的で使うことができそうということが分かりました。特にURLSessionやNotificationCenterといったFoundationのPublisherは機能として既に組み込まれている部分であるので、積極的に置き換えてみて効果を実感してみたいと思います。

最後まで記事を読んで頂き、ありがとうございました。

参考文献

Apple document (Combine)

宣言的? Declarative?どういうこと?

【iOS】Combineフレームワークまとめ

Creating a custom Combine Publisher to extend UIKit

iOS, Swift