iOS/Android別々で開発しているVoicyアプリの処理を揃えるために始めたこと

この記事はVoicyアドベントカレンダー14日目の記事です。
誰がなんと言おうと14日目の記事です。

いよいよ寒くなってきましたね!みなさまいかがお過ごしでしょうか。
私はBiSHの解散を聞いて数日落ち込んでいます。
Voicyはオフィスが道玄坂にあり、BiSHの所属事務所も道玄坂にあるので、いつかばったり出会してしまうのではないかと通勤時はいつもそわそわしてしまいます。

というわけで本日の記事は私ほーりー(@horitamon)の担当です!
VoicyのAndroid版アプリの開発を担当しています。
最近は社内でバンドを組んだり?、そのTシャツが制作されたり?と、精力的に音楽活動も行っています。

今年のVoicyアプリも新機能のリリースが盛りだくさんだったわけですが、その中で私がVoicy4つのアプリの処理を揃えるためにはじめたことについて語っていきます!
技術的な細かい点については別記事で語ることにして、今回はそれぞれの目的や、やってみての気付きを中心にまとめていきます。

4つのアプリでひとつのVoicy

Voicyはパーソナリティさんが話す空気感をそのままに、手軽にリスナーの皆様まで届けられることが特徴の音声プラットフォームです。

そのため、リスナーが放送を聴く再生アプリ「Voicy」と、パーソナリティさんが収録で使うアプリである「Voicy Recorder」と、アプリが二つに分かれています。
またVoicyはサービス開始当初スマートフォンアプリはiOSのみの対応で、Androidアプリは後追いでの開発となったこともあり、現在AndroidアプリとiOSアプリはそれぞれネイティブで、完全に別のソースコードを書いて開発しています。

つまり新たにVoicyに新機能を追加しよう!となると、4つのアプリに対して実装が必要になるわけです。

機能によってはiOSを先行リリースし、Androidは後追いでリリースする(あるいはその逆)という方法も取っていますが、今年リリースした目玉機能「生放送」のように大きな機能を同時に公開する場合には、4つすべてのアプリが連携し、異なるOS間でも同じ挙動を実現できるように開発していく必要があります。

そんな中で、それぞれのアプリの「質」をそろえるために今年アプリチームで取り組み始めたことについて、詳しく書いていきます。

それぞれのアプリの処理を揃えるために始めたこと

①Kotlin Multiplatform Mobileを用いたAndroid/iOSソースコード通化

先に書いた通り、AndroidiOS、収録アプリと再生アプリでは完全に別のソースコードを書いて開発しています。
ここで出てくるのが「それぞれのアプリで微妙に処理が違う」問題です。

昨年までのVoicyでは収録放送しか対応していなく、(言い方は悪いですが)最悪バックエンドチームが作ってくれたAPIを正しく呼んでさえいれば、細かい処理は異なっていても許容できる状況でした。
ただし今年リリースした生放送では、

  • 開始、終了、ゲスト入退室といったイベントを各OS/アプリで同期する必要がある
  • 音声がリアルタイムで公開・収録されるため、エラー発生時のハンドリングを丁寧に行う必要ある

といった点から、より細かく処理の内容や順番をiOS/Android間、収録/再生アプリ間で合わせる必要が出てきました。

もちろん設計を丁寧に行って各担当者で共有できていれば問題はないのですが、スピードを求めるとなると細かいところまで設計はできませんし、開発の途中でわかっていく部分も大いにあります。
またそれぞれのアプリを別のエンジニアが実装すれば、実装ミスもあれば設計/仕様の解釈違いも生まれます。  

生放送に関わる部分だけでも、処理を共通化できたらなあ…と考え、Kotlin Multiplatform Mobileの導入を検討しました。
(ここからは略してKMM)

KMMは、ざっくり言うと「KotlinでAndroidでもiOSでも動くライブラリをつくれる」フレームワークです。
UIを共通化することは目的としておらず、ビジネスロジックを共通化することを目的としています。
kotlinlang.org

実際に生放送の開発ではKMMを用いて実装を進めましたが、結果的に現状iOSではKMMを使用した生放送ライブラリは導入しておらず、Androidの再生/収録アプリでのみ使用しています。
Kotlinで書けるし、作るのもビジネスロジックだけなのでそんな難しくないだろ〜〜とか思ってましたが、そんな訳なく、しっかりとハマりどころがありました。

  1. モジュール構成に悩む
    公式のサンプルではAndroidアプリ・iOSアプリ・KMMモジュールがひとつのプロジェクトファイルで管理されており、AndroidStudioでKMMプロジェクトを作成する際にもすべてまとまってプロジェクトが作られます。
    しかしVoicyではiOS/Android、さらに収録/再生でリポジトリが分かれているため、どうやって各アプリから参照を追加するか悩みどころでした。
    最終的にAndroidではチームラボさんの事例を参考に、MavenリポジトリをビルドしてGitで管理し、submoduleで紐づける方法を取っています。
    speakerdeck.com

  2. ライブラリによってはcocoapodsの参照がうまく追加できない
    KMMではiOS向けのサードパーティライブラリも、cocoapodsを用いて参照を追加できます。
    生放送では音声通信でサードパーティライブラリを使用することになっていたのですが、それへの参照を追加がなかなかできず…
    こちらの原因と対処については別記事にまとめる予定です。

  3. やはりiOSアプリ作ったことが無いと色々と時間がかかる
    二つ目の内容と重複する部分もありますが、やはりiOSの勝手がわからないと色々と時間がかかります。
    cocoapodsもAndroiderには全く馴染みがないので、普通はどうするのかわからない状態でKMMのやり方を調べるのは難しいものがあります。
    また本体はKotlinで書けるとは言え、iOSで動作確認をするとなるとテストアプリの実装はSwiftで書くことになり、そこでも地味に時間がかかります。

これらの理由でiOSでの動作が保証できるまで時間を要す状況だったため、一旦リリースまでの共通化は目指さず、リリース後にAndroidでちゃんと動くことが確認できてからiOSへ導入する機会を窺うことにしました。
ここでは課題だけ書いてしまいましたが、今後も引き続き導入に向けて動きつつ、より細かい内容を公開していければと思っています!

②Firebase Analyticsのイベントログの実装共通化・自動化

この施策は、ABEMAさんが実践したアプリ行動ログの自動生成システムづくりの事例をもとにしています。

developers.cyberagent.co.jp

Voicyではユーザーの行動ログをFirebase Analyticsを用いて集計しています。
基本的に新機能開発の序盤で社内のデータチームが集計したいイベントのログ設計を設計書に書き起こし、iOSAndroidでそれぞれ実装する、という流れです。
おおむね上のABEMAさんの事例と同じ方法・状況でした。
特に私たちが課題に感じていた点は下記のものたちです。

  • イベント名やパラメータのアルファベットを微妙に間違える・そのままリリースしてしまいデータが正しく取れなくなる

目視で設計から実装に書き移しているのでミスが防ぎきれないだけでなく、画面や機能などサービス固有の名詞を使っている場合にはタイポを無視する場合もあるため、レビューでも文字の誤りを見過ごしやすい状態にあります。
この誤りをそのままにリリースしてしまうと、iOSAndroid両方でそろえて取りたかったデータが正しく取れず、機能改修の効果が測れずプロダクト開発に影響が出るだけでなく、他社様とのタイアップ・コラボ案件の効果報告が正しくできないといったビジネス面での影響も出てしまう恐れがあります。

  • 単純作業でつらい

イベント名をつけて必要なパラメータとともに送るだけの簡単なログ実装作業ではありますが、上の内容を気にしながら大量にログを実装するとなると、シンプルにつらいものがあります。
特に新機能追加に伴うログ追加の場合、各OSのメンバー総出で実装することもあります。

これらの解決を目指し、ログ自動生成プロジェクトに着手しました。
実際にやり方を考えていく上で、参考事例と全く同じ方法は取らず、Voicyで今やっている方法の延長線上で実現できるようアレンジしています。

まず最初にデータチームが書き起こす設計書は、参考事例ではConfluenceに起こしていましたが、既にスプレッドシートに書き起こして運用しているものがあるため、それをベースに、この後の変換処理に必要な情報を書き加えることとしました。
そこからJSON Schemaを生成するスクリプトをGAS(Google Apps Script)で実装し、quicktypeを使用してKotlin/Swiftのクラスに変換しています。
(quicktypeはブラウザでも使えます。JSONJSON Schema、yamlを入れるとプログラミング言語に変換できてたのしい)
app.quicktype.io

こちらのプロジェクトの開発内容はKMMと違って現在開発中で、まだリリースはされていません。
今のところの悩みポイントとしては、

  1. JSON SChemaに指定する型をスプレッドシート上でどう表すか迷う
    画面名を渡すパラメータは自由文字列ではなくenumにする、複数の値を結合して送る場合は一旦リストを引数にする、といった「エンジニアが実装面で楽になる都合」をJSON Schemaの型指定で実現したく、それらをスプレッドシート上で指定し、GASで変換する流れを考えることに時間がかかりました。
    現状のスプレッドシートの形を崩し過ぎず、かつGASでJSON Schemaへ変換しやすいフォーマットがなかなかしっくりきていません。

  2. 設計書いてボタン押したら変換完了!を実現したいが、全体を考えるのがむずい
    現状、手作業で生成していく流れはできつつあります。
    最終的にはスプレッドシートを更新してボタンを押したら、諸々の処理がピタゴラスイッチの如く走り出し、できあがったログイベントのクラスがGitに自動Push、なんならライブラリにビルドされてアプリはバージョン変えるだけでいい、くらいまで持っていきたい気持ち。
    しかしどこの環境で動かそう?どの流れで処理しよう?と考えないといけないことは色々あります。

おわりに

今年の取り組みについて振り返ってみました。
まだまだ道半ばなので、来年はこれらを仕上げてよりアプリの開発をスピードアップしていきたい思いです。
特にKMMはまだまだ発展途上なフレームワークなので、今後の動向も追っていきます。

そしてVoicyのエンジニアチームではテックについてゆるふわで語るVoicyチャンネル「voi-chord」を運営しています!
開発のおともにぜひお聴きください。
voicy.jp

次回はテックリード みっきーさんの記事です!