GameWith Developer Blog

GameWith のエンジニア、デザイナーが技術について日々発信していきます。

GameWithアプリに動画機能をつけた話_2つのアプリの統合 #GameWith #TechWith

GameWithでiOSエンジニアをしているkyamです。

いきなりですが、最新のver2.1.0でGameWithアプリで攻略動画が見られるようになりました! 今回の記事ではGameWithアプリに攻略動画を閲覧する機能をつけた話をしています。

自分は以前Mippleという攻略動画アプリを専任で担当していましたが、プロダクトの方針転換によりMippleの機能を本体アプリに統合することになりました。
この統合の過程で、どのように開発をし、どのような知見を得たのかを共有しようと思います。

統合の背景について詳しく知りたい方はこちらの記事をどうぞ。 tech.gamewith.co.jp

実際の開発方法

1.まずは別アプリとして作る

自分はまず旧Mipple側を統合後のUIに変更し機能などを実装しました。
本体アプリ側もトップネイティブ化という大きなアップデートを実装している最中でもあったため、まずは本体側はいじらずに別個で開発しました。

ただ意識していた点はあり、本体側でどのような設計をしているか、どのようなディレクトリ構成をしているか、どのようなExtensionファイル(実装を便利にするコード群)が用意されているか、各種フレームワークは何を使っているか、SwiftLintの設定をしているかなどを予め確認しそれに合わせてコードを修正しました。

例えば画像キャッシュのライブラリとして、本体アプリではSDWebImageを使っていますがMippleではKingFisherを使っていました。 その為、Extensionのコードなどで以下のようにライブラリに依存している部分を別ライブラリにしたり、ライブラリに依存しないコード(Codableを用いるなど)に変更したりしました。

import Kingfisher

extension UIImageView {
    func download(image url: String) {
        guard let imageURL = URL(string: url) else {
            return
        }
        self.kf.setImage(with: imageURL, placeholder: nil, options: [.transition(.fade(0.3))])
    }
}

別アプリとしてまず作った理由は他にもあり、デザインを見た時にUIが独立しており既存のコードには影響を与えないように実装できるなと感じた点です。

f:id:keeetaka:20190221154907p:plain

上記は実際のアプリ画面なのですが、見て分かる通りコンテナの中の一つのUIViewControllerとして独立しています。
ですので別アプリとしてまず独立して作っても後で綺麗に丸ごとコードを移植できるなと感じました。

実際本体側のコードでは、各タブ情報のenumがあり、項目に合わせて該当するUIViewControllerを表示させる仕様だったのでバッティングせず開発することができました。 下の例だとmipple(任意のkey)情報をAPIから受け取った場合は動画用のUIViewControllerを作成するといった形です。

    func indicatorInfoProviderViewController(for page: WalkthroughPageEntity) -> IndicatorInfoProvider {
        switch page.type {
        case .web:
            // WebView表示用のViewControllerを返す
        case .mipple:
            return buildVideoListViewController()
        }
    }

    func buildVideoListViewController() -> VideoListViewController {
        // 選択されたゲームを動画一覧を表示するViewControllerに渡す
        let videoListVC = VideoListViewController(selectedGame: selectedGame)
        return videoListVC
    }

実際以下のようにMippleのディレクトリをそのまま移すだけで基本的には済みました。 (※画像は開発中のものです)

f:id:keeetaka:20190225160511p:plain

2.UIと機能を統合後にコードレビュー

上記の通り機能とUIが完成後、本体側にコードを移植しました。
その際に本体アプリを担当している2人のエンジニアにコードレビューを依頼し、既存コードの書き方に合わせているかというよりはクラッシュに繋がりそうなコードや、新規で作らなくても既存コードにあるこのメソッド(既に安全性が保証されているもの)を使いまわせるなど、品質に影響を与えないかを中心にまずチェックしてもらいました。

統合を通して得られたもの(知見)

1. 音の取り扱い

以前動画アプリを作った時も感じたのですが、iPhoneのサイレント(マナー)と消音の違いを明確に分かっている人は結構少ないです。 自分もそんなに正確に言語化できるほど把握していませんでした。

サイレント(マナー)

着信音や通知音のほか、キーボードのクリック音に影響を与える

消音(音量調節ボタンで音量が0の状態)

音楽やムービーのサウンド音量に影響を与える

基本的な前提は上記のものなのですが、マナーなのに音が鳴るだったり鳴らないだったり人によって反応は様々でした。
ですので今回のチームで出した結論としては、例えサイレントでも消音(音量が0)にしていなければ音が出る、いわゆる他の一般的な動画アプリに音の仕様を合わせる事にしました。

実装としては、AVPlayerの再生前にtry? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)を設定するだけです。

この辺りは実際のユーザーの声を聴いて再度検討したいと思います。

2. Firebaseの取り扱い

複数プロジェクトを抱えている場合、firebase list で一覧を見ることができ、今何のプロジェクトに自分のアカウントが向いているかがcurrentの印により分かるのですが、その際に firebase use <projectID> でプロジェクトを切り替えられるっていうのを知らなくて少し時間がかかった部分があります。

f:id:keeetaka:20190225180013p:plain

具体的な話をすると、以前Mippleで運用していた管理画面を今回のGameWithでも使い回したいと思い、最初以下のようにJSコードのプロジェクトの設定だけを変えていたのですが、当然ながら向き先を変えてデプロイしないとダメですよというお話です。

export default {
  apiKey: "??????????????????????????????????",
  authDomain: "???????????.firebaseapp.com",
  databaseURL: "https://?????????.firebaseio.com",
  projectId: "????????????",
  storageBucket: "????????????",
  messagingSenderId: "??????????????"
}

またこれは知らなかったのですが、firestoreにはAのプロジェクトからBのプロジェクトにデータを移す機能も備わっているようです。 firebase.google.com

今回はデータ構造が異なったため利用しなかったのですが、Firebaseのステージ環境を構築するときなどに利用できそうなのでチェックしておきます。

3. 色々なメンバーと仕事をする楽しさ

今回動画機能の統合を担当したことで、本体アプリチームのPO・デザイナー・iOSチームに加えて、Androidチームともコミュニケーションを取ることが増えました。
Androidチームも今後動画機能を統合するため、動画機能の技術的な仕様などを主に自分が説明するためです。

せっかく会社にいるのだから色々なメンバーと仕事がしてみたいなと思うタイプなので、新しいプロジェクトを担当するのは面白いです。
自分は今新規事業の動画チームやソーシャルチームのMTGにも基本的に参加しており、この1ヶ月半で関わる人が増えたなあと思いました。

今後の予定

今回の統合を通して、無事本体アプリに動画機能を移植することができました。
リリース後、約5日間が経過していますが新規で開発した動画部分でのクラッシュの件数は0でした! デバッグに協力してくれたメンバーや、クラッシュに繋がりそうな部分を指摘してくれたメンバーにも感謝します。

無事統合が終わりホッとした所もありますが、まだまだやり残したこと、やりたいことがたくさんあります。

1. やり残したこと

アプリでは攻略記事のお気に入りや閲覧した記事を履歴として残す機能があるのですが、動画版は一旦見送りました。 Realmを使ってデータを管理しているのですが、どのように記事と動画のデータを扱うかで少し議論が必要になったためです。

こちらは次のversionに含む予定なのでお楽しみに!

2. やりたいこと

アプリではElasticsearchを用いて内製した独自の記事検索システムがあるのですが、これを動画にも適用したいなと思います。
現状動画データはFirestoreで管理しており、GameWithのDBなどに動画データがあるわけではありません。 Firestoreにも外部の Algolia などを用いることにより検索の仕組みを作ることができるのですが、一回の検索で別々の場所にある二つのAPIを叩くのは流石に非効率です。

こちらは社内のエンジニアと相談し、良い形に落とし込んで実装したいなと考えています。

検索機能について知りたい方はこちらの記事をどうぞ。 tech.gamewith.co.jp

終わりに

現在弊社ではいくつかの新規事業が並行して立ち上がっています。 今回実装した動画機能も元々は新規事業としてスタートしたものです。新規開発に興味があり少しでも話を聞いて見たいと思った方は是非Wantedlyで会いに来てください!

www.wantedly.com

またGameWithのDeveloper向けTwitterアカウントも開設しました。
社内イベントの告知やブログの更新情報などを発信するので良かったらフォロー宜しくお願いします!

twitter.com

今回の記事が少しでも多くの方の参考になれば幸いです。
ここまで読んで頂いてありがとうございました。