ニコニコ動画iOSアプリ 改善の歴史


はじめに

こんにちは。ニコニコ開発部で、動画スマホアプリ開発を担当している秋元です。

私たちのチームでは、いくつかのスマホアプリを開発しています。
その中でも、ニコニコ動画iOSアプリは、10年以上に渡り開発・保守・運用を行っている歴史あるアプリです。

この記事ではその歴史を振り返りながら、私たち開発チームがどのように改善を進めてきたかについて紹介します。
大規模なiOSアプリ開発の楽しさや大変さ、そしてやりがいが、読んでくださる皆さんに伝われば嬉しいです。

iOSDC 2023 のチャレンジトークンが記事内に 2つ 含まれています。ぜひ探してみてください!
(※ 記事の見出しにの横についている「#」はチャレンジトークンではありません。チャレンジトークンは文中に配置されています。)

ニコニコ動画iOSアプリとは

ニコニコ動画iOSアプリは、iPhoneやiPadなどのiOS/iPadOSデバイスでニコニコ動画を視聴できるアプリです。
コメント付きの動画視聴、コメント投稿、検索、ランキングなどをメイン機能として持っています。
また、ニコニコにある様々なサービスやコンテンツにアクセスするための「ポータル」としての役割も担っています。

その歴史はかなり古く、初期リリースは2009年で、今年で14周年を迎えました。 2009年といえばiPhone3GSが発売された年です。
コードベースは、総Swiftコード行数は 39万、総コミット数は 5.8万と、比較的大規模なiOSアプリと言えるかと思います。 (主要リポジトリのみ、テストコードを除く)

まず、10年前のアプリ構成からふりかえっていきます。

ハイブリッドアプリ時代(〜10年前)

今から10年前、2013年頃のニコニコ動画iOSアプリは、純粋なネイティブ実装のアプリではなく、いわゆるハイブリッドアプリと言われる構成でした。
ハイブリッドアプリとは、WebViewを駆使して、HTML/CSS/JavaScriptを用いて画面や機能を構成するネイティブアプリのことです。
ニコニコ動画iOSアプリの場合だと、動画プレイヤーはネイティブ実装で、動画リストなどそれ以外のUIはWebView上でHTML/CSS/JavaScriptを用いて組み立てていました。

この構成の主なメリットは、ちょっとしたUI改修の際に、Apple審査を通す必要が無いというのがありました。
今だと、ほとんどの場合は、1〜2日程度で審査結果が返ってきます。
しかし、当時はApple審査へ提出してから通過するまでにもっと時間がかかるものだったので、妥当な選択だったと思います。
詳細は当時の資料を参照してください。

以下は、当時のWatch画面です。
動画情報が表示されるViewはWebViewの上で構成されていて、右上の映像Viewだけがネイティブで実装されたViewになっていました。

image_100

顕在化してきた課題や問題点

しかし、それから数年間運用を続けていく中で、ハイブリッドアプリの課題や問題点が段々と目立つようになりました。
それに加えて、当時のiOSは毎年多くの機能追加があり、iOSバージョンが上がる度にiOS SDKやAPIの仕様や挙動が頻繁に変わりました。
アプリも当然その影響を受けるので、コードを修正して追従していくわけですが、これを繰り返していくうちに技術的な負債も溜まっていきました。

具体的には、以下のような問題が顕在化してきました。

  • UX上の問題
    • アプリの動作が全体的に重い
    • 動画視聴が不安定
  • 開発上の問題
    • デバイスサーバーの性能がボトルネックになるケースが増えてきた(詳細は後述)
    • Objective-C/Swiftだけでなく、HTML/CSS/JavaScript、サーバーサイドアプリケーションの実装についても把握が必要なため、プログラム全体を把握しづらい
    • 開発環境の構築に手間がかかる
    • iOSが提供するネイティブアプリ向けの新APIの恩恵を受けられない
    • アプリのUIが古くなってきたが、変更しづらい

これらを解消するべく、私たちは、フルネイティブなアプリに作り替えていくことを決断しました。

大規模なリファクタリングには、まとまった期間が必要になります。
その間、機能追加や改修をストップ出来るのが理想ではありますが、事業運営の観点から受け入れ難いものです。
そこで、時間をかけて段階的にリファクタリングしていくことで、それらを両立させる選択をしました。

プレイヤーの内部処理を刷新

アプリのメイン機能である動画視聴の安定性を向上させることが急務であると考えて、ネイティブ化よりも先に、 プレイヤーの内部処理の改善から始めました。
視聴に至るまでの処理フローおよびプレイヤーロジックの改善です。
ニコニコ動画iOSアプリは、プレイヤーで動画を再生開始するまでに、複数のサーバーAPIにアクセスします。
また、再生中も、動画データをサーバーから順次取得したり、コメントを流したりと様々な処理をしています。

アプリのUIは変えずに、プレイヤーのコアライブラリを刷新し、同時にコメント描画ロジックを刷新しました。
また、動画再生の認証処理のためにアプリ内プロキシを使用していましたが、その部分が原因で動画再生が稀に失敗してしまったりと安定性に悪影響を与えていたこともあり、別の仕組みに置き換えました。

開発期間は、6ヶ月ほどかけて実施しました。
この改善の効果としては、それ以前のバージョンと比較して4分の1程度までクラッシュ数を下げることができました。

image_200

コメント描画ロジックを刷新

ニコニコ動画において、最も特徴的な機能がコメント描画です。
映像の上にコメントが流れることで、視聴者の反応がわかり、動画視聴がより楽しいものになります。

コメント描画機能は、動画アプリだけでなく、生放送アプリにもありますが、当時はそれぞれ別々のロジックで実装されていました。
これだと拡張しづらい状況にあったため、ライブラリとして一から作り直しました。
これにより、PC版などの他クライアントと近しい挙動ができるようになりました。

また、このライブラリは、社内で他アプリとも共有していて、生放送アプリでも活用されています。

フルネイティブアプリへの段階的な移行

ここからは、いよいよ #アプリのフルネイティブ化 に向かってリファクタリングを進めていきました。
機能追加や改修とリファクタリングを両立させるため、画面ごとに置き換えを進めていきました。
UIや機能を刷新するプロジェクトが立ち上がる画面については、その案件の中でネイティブ化していきました。
それ以外の一部の画面については、UIを極力変更せずに、ネイティブ実装へ置き換えていきました。

レイヤードアーキテクチャにして、各コンポーネントの依存関係をシンプルにしました。
また、新たに作成するコードはSwiftで書いていき、旧来のObjective-Cコードは移行用の変換層を通して依存関係を持たせることで、現行コードに入る変更の影響をなるべく受けないようにして開発を進めました。

変換層を挟むイメージ図です。

image_300

アプリ内のレイヤー構造を示す当時の資料です。

image_310

そこから、2017年ごろから始めたネイティブ画面化は、約4年ほどかけて全画面移行を達成しました。

XcodeGenでxcodeprojを管理しやすく

Xcodeを使った開発で避けて通れないのが、xcodeprojの管理です。
プロジェクトの設定を持つXMLファイルで、ファイル追加/削除やプロジェクト設定の変更などXcodeのUI上で行った操作によって更新されるため、複数人で開発しているとコンフリクトしやすい上に、解消が非常に難しいという厄介なファイルです。

この問題に対して、XcodeGenを導入して解決しました。
XcodeGenは、YAMLファイルでプロジェクト設定を書き、そこからxcodeprojを生成することができるので、コンフリクト解消がとても楽になりました。
ニコニコ動画iOSアプリでは、前述のコンポーネントごとにターゲットを分けています。
XcodeGenには、ターゲットをテンプレート化する機能があるので、これを利用することで重複した記述を減らしてシンプルなYAMLファイルにできています。

チーム内で導入イメージのやりとりをした時の資料です。

image_350

デバイスサーバー依存を減らす

このネイティブ化は、UIがネイティブ実装になるだけでなく、デバイスサーバーへの依存を減らすといった目的もありました。
デバイスサーバーとは、社内用語で、スマホアプリなど特定のデバイス向けに特化したレスポンスを返すサーバーのことを指します。
ニコニコ動画iOSアプリ用のデバイスサーバーでは、専用のAPIや画面を表示するためのHTML/CSS/JavaScriptを持っていました。

image_410

また、デバイスサーバーは、ニコニコのコアシステムからデータを取得する際に、クライアントアプリとの通信を単純に中継するだけでなく、必要に応じてデータのキャッシュ機構も持っていました。
負荷を軽減したり、クライアントアプリに素早くレスポンスを返すために活用されていました。 しかし、アプリの利用者が増えていくにつれて、アプリから大量のアクセスがあった際にデバイスサーバーの性能がボトルネックになってしまったり、サーバーの故障などの障害も度々発生するようになってしまいました。

image_420

保守運用の観点からも、デバイスサーバーは悩ましい存在になっていました。
デバイスサーバーの改修は、iOSアプリ開発チームで行なっていましたが、iOSアプリ開発の知識(Objective-C/Swift)はもちろん、HTML/CSS/JavaScript、サーバーサイドアプリケーションの実装、サーバー構築や運用など幅広い知識と経験が必要になり、エンジニアの負担も大きくなりがちでした。
WebViewは、パフォーマンスの面で問題があったり、当時は毎年のiOSメジャーバージョンアップごとに多くのAPIや挙動が変わり、ネイティブの部分とWebの部分のそれぞれの影響を受けやすい状況でした。
また、iOSに便利なAPIが提供されていきましたが、それらを活かしきれない歯痒さもありました。(主にUIKit関連のアップデートや、SizeClass、ダークモード等々…)

これらの事情を鑑み、デバイスサーバーからの依存を減らしていくことになりました。
その結果、デバイスサーバーを経由せず、ニコニコ動画のコアシステムに直接アクセスすることで、実装がシンプルになり、視聴開始までの処理速度も向上しました。

image_430

OpenAPI仕様からAPIクライアントを自動生成

アプリからアクセスするAPIが新しくなったことで、もう1つ大きなメリットが生まれました。
API仕様が、OpenAPIのYAML形式に対応したため、openapi-generatorを用いてAPIクライアントのロジックを自動生成できるようになりました。
詳細については、こちらの記事を参照してください。

UI部品をコンポーネント化

複数の画面で利用されるUI部品をコンポーネント化しました。
また、UIの動作確認がスムーズに行えるよう、UIカタログの仕組みも用意しました。
本体アプリを起動せずとも、カタログアプリを起動してUIコンポーネントの挙動が確認できるというものです。

Watch画面改善

ネイティブ化を進めていく中で、最も時間をかけて移行した画面がWatch画面です。
Watch画面とは、動画視聴するための画面で、ニコニコ動画iOSアプリで最も重要な画面です。
映像を流すプレイヤー部分と、その動画のタイトルや説明文、コメント、関連動画などの動画情報枠とで構成されています。
デバイスの向きやコメント投稿状態などで様々な表示状態を持っているため、かなり複雑な仕様になっています。

当時のWatch仕様の検討資料の一部です。

image_540

5つのフェーズに分けて徐々にリリースしていき、理想型を目指しました。

フェーズ 主な改善内容
Phase1
  • 今後の改善に向けてベースの実装を刷新
  • Watch画面のデザイン変更、10秒シーク、レジューム再生、NG設定 等の機能を追加
  • Auto Layout対応
  • iPhoneXのSafeAreaがある端末への対応
Phase2
  • 情報画面をポートレートで見られるようにする
  • 情報画面をランドスケープ(左右分割表示)で見られるようにする
Phase3
  • 説明文の開閉
  • 広告表示などの修正
Phase4
  • プレイヤー設定追加
  • 反転再生
  • コメント透過
  • コメントNG設定
Phase5
  • 旧Watch情報viewの廃止
  • ながら見機能追加
  • Watchエラーまわりの仕様改善
  • 自動再生のON/OFF設定の追加
  • ピンチイン/ピンチアウト操作の追加

左が旧プレイヤー画面で、右が現在のプレイヤー画面です。

image_500

広告SDK

ネイティブ化により、もうひとつ大きく変化したのが、広告表示の仕組みです。
ハイブリッドアプリ時代は、HTMLで画面を構成していたため、バナー広告はiframeによる埋め込みで実現していました。
ネイティブアプリになってからは、広告SDKを組み込むように変わりました。
ハイブリッドアプリの時代には、複数の広告SDKを導入することの負担を解消するためにiframeを用いていましたが、現在の広告SDK界隈には、メディエーションと呼ばれる仕組みがあり、複数の広告SDKをひとつの処理でまとめて扱うことができるようになっています。

また、プレイヤーで表示する動画広告とバナー広告を連動させる仕組みもネイティブアプリならではのものです。

image_600

iOSネイティブアプリならではの機能

アプリをネイティブ化したことで、iOSならではの機能に対応しやすくなりました。
以下に、その代表的な機能をいくつか紹介します。

今後の課題

今後、開発的に実現したいことについても少しだけご紹介します。

Swift Concurrency移行

これは既にプロジェクトの一部に適用済みですが、最終的にはアプリ全体に適用したいと考えています。

Swift Package Manager移行

現状は、CocoaPodsを利用していますが、これをSPMに移行させる予定です。

SwiftUIの活用

UIKitで組み立てている画面が大半ですが、今後はSwiftUIを使った画面を増やしていきたいです。

おわりに

#ニコニコ動画iOSアプリの歴史 をふりかえってみましたが、いかがでしたでしょうか?
新規アプリ開発のような華々しさはありませんが、大規模なアプリを改善していく事は、大きなやりがいと楽しさがあります。
特に、新機能や改善をリリースした時に、利用者の方々から多くの反響を頂けるというのは、プレッシャーでもありますがとても貴重な経験です。
そして、多くの方々から喜びの声を頂けた時は、エンジニアとして何ものにも代え難い喜びがあります。
これからもニコニコサービスを、楽しく気持ちよく使って頂けるように、より良いアプリを開発していきたいと思います。

iOSDC 2023のチャレンジトークンは、以下の記事にも含まれています。
教育事業のiOSアプリ開発について紹介していますので、ぜひご覧ください。
https://blog.nnn.dev/entry/2023/08/23/110000


ドワンゴでは、ニコニコ動画を一緒に作ってくれる仲間を募集しています。
iOSやAndroidのネイティブアプリ開発が好きな方、ドワンゴに興味がある、または応募しようか迷っている方がいれば、気軽に応募してみてください。