『ニコニコ漫画』『読書メーター』スマートフォンアプリについて【トリスタinside出張版】

 はじめまして。トリスタアプリ開発セクションのtukiyoです。普段はAndroidアプリの開発を行っています。

 このブログの読者の皆さんには、「トリスタ」という名前に聞き覚えがないかもしれませんのでご紹介させていただきますと、株式会社トリスタは『ニコニコ漫画』『読書メーター』など、本にまつわる事業を担うカドカワのグループ会社です。私を含め、メンバーの大半はドワンゴの社員でもあります。

 実は先日、トリスタでもブログ「トリスタinside」を始めました。今回はトリスタの紹介と、ブログの宣伝……だけでは物足りないので、こちらのブログにも私が書いた記事を掲載させていただくことになりました。内容はほぼ変わらないので、元記事でお読みになる方はこちらからどうぞ!

 本記事では、トリスタで提供しているスマートフォンアプリのうち、『ニコニコ漫画』と『読書メーター』がどのように作られているか概説します。より詳細な内容については別の機会に譲り、今回は触りの部分だけ紹介します。自分の担当であるAndroidを中心として話を進めていきますが、両プラットフォーム共通の取り組みなどにも触れていきたいと思います。

 以下を読むにあたって、『ニコニコ漫画』(2015年リリース)の後に『読書メーター』(2017年リリース)が開発された、という変遷を念頭に置いておくと、理解の助けになるでしょう。

ニコニコ漫画

ニコニコ漫画アプリ

どんなアプリ?

ニコニコ漫画は雑誌やWEBで話題のマンガを読める「niconico」の漫画アプリです。
作品数は国内最大級、毎日いろんなマンガが楽しめる。ここでしか読めない作品を思う存分楽しもう!
(AppStore)

 雑誌連載作品のみならず、ユーザーの制作された多数の漫画がコメント付きで楽しめるのがニコニコ漫画です。最近大きく盛り上がっている異世界系作品を始めとして、他サービスには見られない独特の作品層を形成しています。ここから商業デビューを果たしたりアニメ化した作品もあるなど、魅力的な漫画が満載です。

 特徴的なのは漫画ビュアーで、前述の通りニコニコらしい流れるコメントはもちろんのこと、縦スクロールしているだけで見開きページがシームレスに見れたり、紙芝居のように編集された音声つきの形式(ニコニコ形式)が読めたり、新感覚の漫画読書体験を提供する大きな柱を担っています。

 ニコニコ漫画サービス自体は2010年からあるサービスですが、2015年秋にAndroid,iOS共にスマートフォンアプリが公開される運びとなりました。つまり執筆現在でもうじき3歳になるアプリなわけですが、ありがたいことにストアでも温かい評価をいただいておりまして、おかげでさまで今もすくすく元気に育っております。

 では、アプリの詳細について触れていきましょう。

使用言語

 iOSでは全て Swiftで、Androidでは JavaKotlinが半々となっています。

 Swiftはモダンな言語を求めて当初から採用していました(当時ver1.2)。ただ、後にバージョンアップデートへの追従作業が待ち受けていたわけですが、後続の読書メーターアプリではこれを重く見て Xamarin(C#) を採用しています。

 Androidは開発開始時点でちょうど現在便利に使われている様々なツールの黎明期でもありました。言語もその1つであり、現在進行系でKotlinへの書き換え作業を行っています。執筆現在ではActivityやFragmentといったUI層と、Repository層の書き換えの大半が完了しており、保守や機能追加により柔軟に対応できる体制を整えています。本当はEntityの類こそKotlin移行(特にnullableの導入)で最も恩恵を得られる場所かとは思うのですが、そうであるが故に慎重にならざるを得ないのが辛いところです。

アーキテクチャ(Android)

 現在は MVVMをDatabindingと共に採用しています。ニコニコ漫画アプリではユーザー入力とその反映はそこまで複雑でも多くもないのですが、採用した読書メーターでの感触のよさをフィードバックしました。ちょうどKotlinの書き直しの流れに乗った形になりますが、Architecture Componentsはまだ様子見の段階であったために採用は見送り、自前のViewModelを利用しています。

Reactive Programming

 iOSでは RxSwiftRxCocoa を、Androidでは RxJavaRxLifecycleを用い、Reactive Programmingを取り入れた開発を行っています。この開発に携わる前までは座禅を組んだ犬がいることしか知らなかった私ですが、RxJava 2.xへの対応も終え、APIリクエストにイベント伝播に、と今ではRxを手放せません。

 AndroidでMVVMとRxを組み合わせたとき特有の、プロパティの変更イベントをいい感じに扱ったり直接Viewに反映させたい悩みに対しては、漫画アプリではObservableFieldを少し拡張し、RxJavaを利用してObservableとの相互変換を実現するライブラリを作って使用することで解決しています。似たようなライブラリにRxPropertyがありますが、こちらはそれと比較して軽量であることを志向しています。

見開き表示

 電子書籍リーダーで漫画を読む際、難点の1つが『見開きページの感動が薄れる』ことではないでしょうか。見開きが左右に分割されて2ページ扱いになって、行ったり来たりしないと見れないことはとてもストレスであり、分かたれるべきではないページが分かれる表示は作者の意図に沿っているとも言い難いです。

 ニコニコ漫画のスクロール形式ビュアーは縦スクロールでページが上下に流れていく形式なのですが、見開きを考慮しない場合、見開きページの右半分の下に見開きページの左半分が表示される、なんとも悲しい表示になることでしょう。そこを我々は、『縦スクロールをすると見開きページだけ左右にスクロールされているように見える』という機能1を搭載することで解決しています。

 実際に見開き表示を確認してみたい方は、アプリから 幻想武姫ナガシノ などでお確かめください。

 見開きの間だけ無理やり横スクロールさせているわけではなく、スクロール量に応じて見開き原稿のほうを適切に動かすことで実現をしています。これにより、主人公がキメている見開きも、タイトルページのカラー見開きも、指を上下に動かすだけでシームレスに読むことができます。

読書メーター

読書メーターアプリ

どんなアプリ?

読書メーターは日々の読書量を簡単に記録・管理できるアプリです。グラフや、本棚、1,800万件を超える感想・レビューも。新たな本との出会いや読書仲間とのつながりが、読書をもっと楽しくします。
(AppStore)

 2008年に開始された、読書記録管理&コミュニティサイトが読書メーターです。読んだ本や読みたい本の管理を行うことができる他、自分が読んだ本に、他の人が似た感想を抱いていたり、あるいは異なる視点の人とコメントで意見を交わしたり、本を介したコミュニケーションを行うことができます。

 扱っている本は自動的に増えていくのですが、商業出版されていないオリジナル本は自分で登録できます。私も自分が書いた本を登録していて(大半はいつの間にか読者の方が登録してくださっているのですが)、『自分が読んだ本と同じ本を読んだ人の感想が表示される』という共読機能により、自分の本の感想が勝手に流れてくるという最高の著者体験をユーザーの一人として享受しています。

 PC版SP版がリニューアルされて今の姿になる以前からiOSアプリも存在していたのですが、Android版の開発に合わせて5年ぶりの全面リニューアルを行っています。どちらも2017年秋に公開されました。

iOS版開発

 iOSは前述の通り、 Xamarin.iOSで開発されています。実は旧バージョンはTitaniumで作られていたので、時を越えて同様のクロスプラットフォームフレームワークが採用されたことになります。……が、これもまた時を越えて同様に、Androidとの同一ソースを試みたわけではありませんでした。

 本項については以下の記事が詳しいので、そちらをお読みいただければよいかと思います。

読書メーターiOSアプリにおける Xamarin.iOS 導入事例のご紹介
https://qiita.com/gomi_ningen/items/ce20176d86276fce71e3

ニコニコ漫画の知見を活かして

 Xamarinの選択を始め、Reactive Programmingの導入やAndroidにおける100% Kotlin開発など、ニコニコ漫画アプリ開発で得られた知見やよい技術はそのまま活かしています。車輪の再発明もしないよう、普遍的なviewについてはたとえば以下のようなライブラリに切り出して利用しています。

アーキテクチャ(Android)

 読書メーターからMVVMを採用しています。Databindingのお供には RxProperty を使い、ユーザー入力の多いSNS系アプリをスマートに開発できました。APIから得られた値をViewModelのプロパティにsetするだけで、適宜加工されたmodelがよしなにリストに反映されたり、複数の入力状態に依存するボタンの状態を全てstreamで表現できたりと、とても恩恵を受けています。

 また読書メーターでは、ユーザーの投稿した感想やつぶやきに「いいね!」をつけることができます。それを始めとした状態の効率的伝播のために、イベント回りは RxBus2 に任せています。もちろん、通知が欲しい画面が任意に購読すればいい代わりに、グローバルなイベントになってしまうので一長一短ですが、適宜イベント用のinterfaceを切ることで秩序を保っています。

Swagger

 読書メーターでの技術採用事例で最も開発に寄与しているものといえば、このSwaggerでしょう。読書メーター両アプリでは、APIサーバーより提供されるAPI定義ファイルから、 swagger-codegen を使って自動生成されたAPIクライアントライブラリを利用することで、大幅な工数の削減、機能開発への注力を実現しています。アプリ用にAPIを提供してもらっていることも相まって、いわゆるData層には、ライブラリのメソッドを呼ぶだけで終わるRepositoryがほとんどです。

 結合が完了するまでのAPIが未完成の状態で、Swaggerは特に力を発揮します。読書メーターは先にPC版SP版のリニューアルが行われていて、既存APIもSwaggerによるAPI定義が見られる状態だったため、

  • 既存のAPI定義を参考に、アプリチームがアプリ用APIのSwaggerファイルを書く
  • Pull Requestを出して、必要ならサーバーチームとも議論
    • この際、コード生成できるvalidな状態かCIでチェック
  • マージされる
    • CIがコード生成して社内リポジトリ(artifactory)にSNAPSHOTとしてpublish
    • CIが最新のAPI定義を使ってモックサーバーを立てる(with pokemock)
  • 手元で依存解決して、新しくできたAPI用のメソッドを呼ぶ

という流れができあがっていました。Swagger定義も複数人開発に対応できるように、ファイル分割して記述できるようもしていたため、アプリチームのメンバーが自分で足りないAPIのモックを作って開発を進める、ということが可能になっていました。

 結合も、モックに向けられたSNAPSHOTライブラリのメソッドから、次々できあがっていくアプリAPIから自動でpublishされてくるライブラリのメソッドに向け直すだけで済むのでとてもスムーズ2です。何かの間違いでAPIの認識が食い違っていても、それはAPI定義から生まれるインタフェースの違いという形で現れるので、コンパイラが怒ってくれます。

 これのおかげで、高い品質のアプリをリリースするための時間が生まれたと言っても過言ではありません。codegenのKotlin版はまだ発展途上ではありますが、Java版であっても十分利用する価値があります3。途中から入れるには少々ハードルが高いので、ニコニコ漫画では自前でAPIクライアントを書いているのですが、読書メーター開発後はもどかしい気持ちに苛まれています。

まとめ

 触りだけとなりましたが、『ニコニコ漫画』『読書メーター』のスマートフォンアプリがどのように作られているかについての紹介でした。今後は、トリスタinside本体のほうで、より具体的な内容に言及していければと思います。

宣伝

 トリスタでは、本が好きなスマートフォンアプリエンジニアを募集しています。

 採用情報


  1. 特許取得済み。 [return]
  2. これを見越して、モック用のライブラリはパッケージを同一に、idを別にしておく必要がありました。 [return]
  3. swagger-codegenは現在open API specification v3.x未対応の他、fork実装として OpenAPI Generatorも出てきたため、今後の動向が注目されます。 [return]