【Kotlin Multiplatform】commonMainのinterfaceを特定のプラットフォーム向けに拡張する

iOS, KMP, Kotlin, Kotlin Multiplatform, Swift, クロスプラットフォーム

はじめに

こんにちは、iOSアプリ開発グループの永田です。

最近はオードリーのオールナイトニッポン in 東京ドームに参加しました。
ステージ裏の席でしたが最高に楽しめました。円盤化を待ちます。

さて、今回はKotlin Multiplatform(KMP)でプラットフォーム固有の実装を入れる際、
悩んだポイント・辿り着いた解決策についてご紹介します。

モチベーション

KMPは複数のプラットフォームにまたがるロジックを共通化し、そのインタフェースを抽象化できるという点でメリットがあります。
一方でその性質ゆえ、プラットフォーム固有の定義を共通インタフェースの一部として公開することができません。

私が直面した具体例をご紹介します。
KMPでiOS, Androidアプリの動画再生処理を共通化するため、commonMainで以下のような定義を追加しました。

iosMain, androidMainでそれぞれ actualVideoPlayerを実装し、それを VideoPlayerViewModelImplに渡します。
iosApp, androidAppでは interface VideoPlayerViewModelを参照して使う設計です。

architecture_base.png

iOSアプリで動画再生をするには、 AVPlayerViewController VideoPlayerなどの、
動画表示を司るクラスに AVPlayerのインスタンスを渡す必要があります。
この設計だと、iosMainに定義した actual VideoPlayerが持つ AVPlayerのインスタンスをiosApp側まで公開できれば動画を表示できます。

しかし、 AVPlayerはiOS SDKに定義されたクラスであるため、commonMainに定義した VideoPlayerViewModelのプロパティとしては定義できません。これを解決したいと考えました。

当初の案

最初に考えたのは、iosMainで AVPlayerをプロパティとして持つだけの interfaceを定義することでした。
この AVPlayerProvidingVideoPlayerViewModelに同時に準拠するクラスを実装し、画面側へ渡そうとしました。

次に、このクラスを画面側から参照する際に、具象のクラスではなく interfaceに依存するようにする方法を検討しました。
Swiftだと、複数の protocolに準拠していることを &で明示できます。

Kotlinでも同様の機能がないかを調査しましたが、どうやらなさそうだということが判明しました。

複数の interfaceに準拠する interfaceを定義する

そこで、これらの interfaceをまとめた interfaceをiosMainで新たに定義することにしました。
この interfaceに準拠したクラスを実装し、画面側からは VideoPlayerViewModelWithAVPlayerとして参照します。

byで委譲する

ここまでくれば、あとは VideoPlayerViewModelIOSVideoPlayerViewModel, AVPlayerProvidingのそれぞれに準拠するよう実装するだけです。

Kotlinには、 byというキーワードを用いて特定の interfaceの実装を他のインスタンスへ委譲できる機能があります。
これを用いて、 VideoPlayerViewModelへの準拠を VideoPlayerViewModelImplに、 AVPlayerProvidingへの準拠を VideoPlayerへ委譲します。

これで、commonMainで定義した interfaceをiOS向けに拡張でき、動画表示が可能になりました。

architecture_result.png

終わりに

今回は、KMPのcommonMainで定義した interfaceをiOS向けに拡張する方法についてご紹介しました。
iOS以外でもさまざまなプラットフォーム向けに使える手法だと思うので、ぜひ参考にしてみてください。

依存関係を図に起こしてみるとちょっと複雑になってしまいましたが、
interfaceへの依存を遵守することでモッククラスのDIが容易になり、テスタビリティ等の観点でメリットが上回ると感じています。
欲を言えば、複数の interfaceに準拠することを、Swiftのように簡潔に記述できるといいなあと思いました。

また、 byキーワードのような機能はSwiftにはないので、単一の言語ばかり書いていると得づらい知識が身につき楽しかったです。
今後も新しい知識を身につけつつ、それをiOSアプリ開発にも活かせるよう精進します。

参考文献