GameWith Developer Blog

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

Vue Fes Japan 2019にスポンサーとして協賛します&オンラインAMA配信をします #vuefes #GameWith #TechWith

こんにちは。GameWithのエンジニアのtiwuです。

Vue Fes Japan 2019

この度、Vue Fes Japan 2019にスポンサーとして協賛することになりました!

vuefes.jp

GameWithはREFRESHMENTスポンサーとして参加者にドリンクやお菓子などのリフレッシュ提供します。

当日は自分がブースに立っていると思うのでぜひお会いしましょう!

オンラインAMA配信

また、Vue Fes Japan × GameWithでオンラインAMAの配信を弊社スタジオから行うことが決定しました!

f:id:tiwu:20191009104613p:plain

普段聞けないようなことが聞ける貴重な機会なのでとても楽しみです!

現在質問を募集中とのことなのでどしどし質問を投稿しましょう!

詳細は下記noteをご覧ください。

note.mu

最後に

台風接近についての開催対応は下記noteに記載されているので参加者の方はご確認ください。

note.mu

無事開催されることを祈っています。

GameWithでは一緒に働く仲間を募集中です!

毎月もくもく会なども開催しているので、気軽に連絡ください!

twitter.com

GameWith iOS もくもく会 #15 開催しました #GameWith #TechWith #gamewith_moku2

こんにちは!
GameWithのiOSエンジニアのkyamです。
9月26日(木)にGameWith主催で第15回目のもくもく会を開催しました!

GameWith iOS もくもく会 #15

gamewith.connpass.com

今回はテーマとしてiOSを採用しました。 実は今までiOSのもくもく会はGameWithで開催したことがなかったため、今回が初めての開催となりました。

それでも今回は社内1人、社外7人の計8人が集まってくれました!

当日の流れ

f:id:keeetaka:20190930141449p:plain

最初にお知らせ、次に自己紹介と今日のもくもく内容を発表して、軽食を挟みながら2時間程もくもくし、最後に進捗の発表という流れで開催しました!

進捗発表

以下がメンバーの取り組んだ内容です。

  • SwiftUIのTutorialと企画のアイデア詰め
  • リリースしたばかりの個人アプリのiOS13の対応(Dark Modeなど)
  • アニメーションの作成
  • macOS Catalina 10.15 beta 9のDLとSwiftUI
  • WebView周りの技術調査
  • CircleCIの挙動確認
  • 通信周りの勉強、QiitaAPIを使ったモック作成

といった様々なテーマの取り組みのもくもく会でした!

最後はせっかくなので20分ほど質問タイムという形式をとりGameWithについてざっくばらんに質問していただく時間をとりました。

挙がった質問としては、

  • 自分の所属している価値検証チームとはどのようなものなのか(自己紹介で所属している旨を話しました)
  • 企画はどのように生まれるのか
  • 新規事業のクローズ判断について

などビジネス的なものも多かった気がします。

最後に

参加していただいた皆様ありがとうございました!
iOSをこれから始めようと思っている方、始めたばかりの方、がっつり業務で触っている方など色々な方が参加されて雰囲気もよく開催できたかなと思えます。
10月以降も開催していく予定なので、興味のある方は是非参加してください。

告知1

もくもく会終了後も話したのですが、GameWith iOSもくもく会参加者用のslackワークスペースを作成しようと思います。
作業中に気軽に質問などを投げることができ、誰でも返答できるようにし、誰に聞いたらいいかわからないといった状況を無くし、参加者にとって勉強会自体の効率をよりあげたり、勉強会後のコミュニケーションを高めたいという意図からです。

告知2

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

twitter.com

5年以上PHP5で運用されていたFuelPHPで動くGameWithをPHP7.3にバージョンアップしました! #GameWith #TechWith

はじめに

こんにちは!GameWithのサーバサイドエンジニアのkuromokaと、神崎です!

今月約1年かけて進めていた、GameWithのPHP7.3へのバージョンアップが完了しました!GameWithはリリースから5年以上PHP5系で運用されていましたが、ようやくPHP7.3にすることができました。

今回のブログでは、このバージョンアップを行うにあたっての具体的な作業内容やバージョンアップ前と後でのパフォーマンス比較などについてお話します。

実はPHP Conference 2019に同様のテーマでセッションの申込みをしたのですが、残念ながら不採用だったためブログ形式にしました。

fortee.jp

チーム構成

バージョンアップ作業は途中の入れ替わりなどありつつ、おおよそ以下のような5人チームでした。

  • サーバサイドエンジニア:2人
  • インフラエンジニア:1人
  • ディレクター:1人
  • チームリーダー:1人

バージョンアップ前のシステム構成

  • PHP5.6
  • FuelPHP 1.6
  • Ubuntu 14.04

バージョンアップ後のシステム構成

  • PHP7.3
  • FuelPHP 1.6(魔改造)
  • Ubuntu 18.04

FuelPHPについては、バージョンアップ中は最新のPHP7系には対応していなかったため、既存の1.6を魔改造をして対応させました。

なぜPHP7にあげようとしたか?

以下のような理由で、PHP7へのバージョンアップを決めました。

  • PHP5.6のセキュリティサポートが2018年12月末までのため

  • 長年の機能追加によりパフォーマンスが低下し、サービスが不安定になる状態が頻発するようになっていた。そのためPHP7にすることでパフォーマンスが向上して、サービスの安定稼働が見込めると考えたため

PHP7のバージョンアップまでのスケジュール

2018年10月にPHP7バージョンアップチームを結成しました。結成当初はサーバサイドエンジニアとインフラエンジニアの2人体制でスキマ時間に取り組んでいましたが、他チームとの兼任で他の作業が忙しかったこともあり、思うように作業が進みませんでした。

そこで2019年1月頃に入社をした私たち2人が2019年4月頃にバージョンアップチームにJOINすることになり、週に最低1日はバージョンアップチームとしての作業を行う時間を確保してもらうようにしました。

その後は順調に作業が進められるようになり、2019年9月に全ての作業が完了しました。

PHP7の作業方針

タスクの管理は、GitHubのProjectsを利用しカンバンスタイルで管理しました。

ソースの管理は、リポジトリから新規ブランチを切って、そのブランチのコードをPHP5.6並びにPHP7.3両方に互換性のある書き方に修正する形で作業を進めました。

理由として、GameWithで動いているサーバ(WEBサーバ・管理画面サーバ・デプロイサーバなど)のソースコードは既存のリリースフローの都合上、基本的にすべて同じリポジトリかつ同じブランチのため、ソースコードもすべて同じになるためです。

PHP7.3のみに対応したコードを書いてしてしまうと「PHP7.3のサーバでは動くけどPHP5.6のサーバでは動かない」といった状態が発生する可能性があり、各サーバごとに段階的にバージョンアップを進めることができなくなります。そのため上記の方法で作業を進めました。

具体的な作業内容

PHP5.6からPHP7.3の変更点の列挙

https://www.php.net/manual/ja/migration70.php https://www.php.net/manual/ja/migration71.php https://www.php.net/manual/ja/migration72.php https://www.php.net/manual/ja/migration73.php

まずは公式の移行マニュアルをすべて読み、変更点を列挙しました。

エラーの修正

初めはUnitテストで起きたエラーの修正を行いました。

その後、integrityというソフトや、Seleniumで動かしているE2Eテストで起きたエラーを修正しました。

sort系関数の変更

PHP7からソートアルゴリズムが変わったため、sort系の関数の結果がPHP5と同じ順番になるような互換関数を作りました。ソートアルゴリズム変更の影響を受けそうな利用箇所については、作った互換関数に置き換えてテストコードで同じ並び順が保証されるようにしました。 https://www.php.net/manual/ja/migration70.incompatible.php#migration70.incompatible.other.sort-order

インフラ作業

今回バージョンアップに伴い、インスタンスを新しく作り直しました。古いインスタンス(c4)のPHPのバージョンアップをあげることもできましたが、コストパフォーマンスの良いインスタンス(c5)を利用したかったため作り直すことにしました。

本番でWEBサーバを置き換えるときは最初1台だけPHP7にしたインスタンスをELBに紐付けて、問題がないことを確認してから残りのインスタンスも入れ替えるような方法を取りました。

リリース後に起きた問題点

以下のような対応漏れがいくつか発生しましたが、幸いサービスが止まったり見えなくなるような事態はありませんでした。

感想

以前までは多いときに1日3回ほどサービスが止まることもありましたが、PHP7.3へバージョンアップしたことでほぼ発生しなくなりサービスの安定稼働を実現できるようになりました。また、パフォーマンス向上によって起動するサーバの台数を減らせたり、コストパフォーマンスの良いインスタンスを利用できるようになったため、運用コストを大幅に削減することができました。

作業中はUnitテストやE2Eテストで既存の動作を担保できたのは大きい点で、心理的安全性が高い状態で作業ができました。

入社して2,3ヶ月後にバージョンチームにJOINをしたので、ドメイン知識が少なかったりシステムが複雑だっため構成の把握に苦労したこともありました。幸いリーダーのドメイン知識が高かったのでバージョンアップをやりきる事ができました。

バージョンアップ前と後でのパフォーマンス比較

GameWithは毎回サーバでHTMLをレンダリングしているため、今回のバージョンアップのパフォーマンス影響がダイレクトにでました。

Auditsのスコアや、Latency・CPU使用率が大幅に改善されました!

f:id:tiwu:20190925111004p:plain
バージョンアップ前

f:id:tiwu:20190925111033p:plain
バージョンアップ後

f:id:kuromoka16:20190925200655p:plain
Latencyのグラフです。PHP7.3のサーバが増えるにしたがって改善されています。
最終的にはphp5.6の時よりも2倍ほど速くなっています。

f:id:kuromoka16:20190925201902p:plain
CPU使用率のグラフです。紫がPHP5.6・青がPHP7.3で、PHP7.3のほうが低くなっています。
サーバー台数が同じ場合のcpu使用率は半分くらいまで落ちました。

今後の展望

  • PHP7の新機能の利用
  • インスタンスのスペックのさらなる最適化
  • サーバ台数のさらなる最適化
  • PHP7.4にバージョンアップ!?

最後に

まだまだ書ききれなかったエピソードがたくさんあるので、良かったらもくもく会などで会いに来てください!

GameWithのDeveloper向けTwitterアカウントを開設しました。

もくもく会の告知やブログの更新情報などを発信するので良かったらフォロー宜しくお願いします!

twitter.com

UX MILK Fest 2019に参加してきました!UX初心者デザイナーの振り返りレポート #uxmilk_fest #GameWith #TechWith

「デジタルなUXに関わる全ての人のためのUXデザインフェス」にデザイナーが行ってきた

こんにちは!GameWithデザイナーのfukiです。

前回の記事でもお伝えしました、GameWithがスポンサーとして協賛したイベント「UX MILK Fest 2019」に参戦してきましたので、今回は振り返りレポートを書いていきたいと思います!

イベントのUXが良い!

早速会場に足を踏み入れると、そこには芝生のカーペットが敷かれたおしゃれな素敵空間が広がっていました....!

木の椅子が並ぶ後ろにはラグが敷かれたエリアもあり、リラックスしながらお話が聞けたり休憩できたりしました。適度にゆるやかな空間がまさに「フェス」という感じで、肩肘張らずに過ごすことができました。

f:id:fukiworks:20190920150109j:plain

会場にはスポンサーのロゴが掲出されていました!感謝!

f:id:fukiworks:20190920145601j:plain

フリードリンク・フードも大変充実していて、

  • カレー
  • サンドイッチ
  • ジュース
  • お酒!!

を写真も撮らずに全て堪能してしまいました。カレー美味しかった....

どこをとっても主催の方がものすごく来場者のことを考えているのが実感できました。さすがUXイベント!

UX初心者がためになったセッション

今回のイベントのテーマは「UXデザインの解像度を上げる」。

UXってものすごい広い領域を指す言葉なので漠然としてしまいがちですが、どの登壇者の方も一歩踏み込んだ具体的な取り組みについてお話される方が多く、まさにこのテーマを体現しているような素晴らしいセッションばかりでした。

今回はその中でもデザイナーの私(UIデザイナー2年目のUX新人)がとても参考になったセッションをいくつか挙げて振り返ってみたいと思います。

リサーチなしではじめるUXデザイン(合同会社DMM.com 井上 誠さん)

f:id:fukiworks:20190920152536j:plain

ユーザーのことが全くわからない状態でデザインをするのは難しい。でもリサーチをしすぎるとさらに知りたいことがどんどん増えてきて、なかなかデザインに繋がらない....。

そんな時は、自分自身がユーザーに置き換わって、「自分ごと」としてデザインを考えると良いよ!という内容でした。

どんなにリサーチをしても、自分ごとにできていないとデザインにはつながりにくい

リサーチすることが目的ではなく、体験を設計するのがUXデザイナーの仕事であることを忘れない

といった言葉が深く印象に残りました。 もちろんリサーチは大切ですが、自分ごとの主観的な体験にはかなわない....。思い当たる節があります。

自分自身、またチームの仲間を「自分ごと」に持っていくためのコツまで解説していて、実行してみようという気持ちが湧いてきました!

f:id:fukiworks:20190920153023j:plain

BtoBtoCサービスのデザイナーがユーザーに近づくために実施したこと(株式会社ヤプリ 城 由美さん)

スライドを公開してくださってます!

ユーザーの気持ちや要望などが、加工された情報になってデザイナーの元に届いてしまう問題。ユーザーとの距離が離れていると起こるこういった状態への対処法をシェアしてくださいました。

ユーザービリティテストなど「できることを小さく実践する」というのはもちろん勉強になったのですが、一番なるほどと感じたのが「成果を共有すること」についてでした。

成果をデザイナーやチームだけでなく会社に広く公開することで、なぜやるのかであったり次にやることを提示でき、納得感を与えることができます。

そうして納得して腑に落ちてくれた方は、自分たちの業務とのつながりが見えてきて、UXやデザイン業務に対して協力的になってくれるとのこと。

私は別業種、別部署の方と仕事をする機会が度々あり、そのたびに「このデザインはユーザーの為にどう働くの?」というやりとりに難航することがあります。それは効果を裏付ける根拠がなくて、私も自信がなかったから。

私自身が行動して成果を共有すれば、周りの方の意識も変わって、あたらしいしくみが増えてユーザーとの距離が近くなる....!よし、頑張ろう!

コントを使ったワークショップに参加

UX MILK Festではワークショップもいくつか開催され、好きなものを選択して受けることができます。

今回私が選んだワークショップはこちら!

f:id:fukiworks:20190920155620j:plain

株式会社メンバーズ 川田 学さんのワークショップです。

ん〜?なかなかやることがイメージできない....ネタ作りでもするのかしら?と思いながらも、お笑いが好きでコントも好きなのでノリでチョイス!

サンドウィッチマンのコントからモデルパターンを抽出する!

note.mu

ワークショップの詳しい解説は川田さんが書かれた記事をご覧ください!

ここでは私がワークショップを受けた感想をメインに書かせていただきます。

まずワークショップが始まってすぐ、みんなでサンドウィッチマンのコントを5分ぐらい鑑賞しました。無論面白かったので普通に笑ってました。もうこの時点でこのワークショップにしてよかったなと思いました。

その上で川田さんがこのコントを分析して、感情が動いたり笑いが発生するパターン要素を16個まとめてくださいました。

私たちはこの16個のモデルパターンを使って、既存のカフェでの体験をリデザインする、という内容でした。

川田さんの資料がすごすぎて、受講者のみなさんがどよめいていた気がします。これを参考にすれば誰でも1本ネタが書けるんじゃ???

同じチームになった方がとてもお話を優しく聞いてくださる方で、ポンポンアイデアが出て楽しかったです。(昼から飲んでいたチューハイが4缶目に差し掛かってエンジンがかかっていたのもあるかもしれません。リラックスしすぎ!)

まさかの発表者に選抜

最後はなんと代表としてみなさんの前で発表することになりました。声が大きかったからかな....

モデルパターンの「最低、最高」を演出するために、カフェの並び列までは何もせず、スタッフの前に立った時から体験を提供します〜まではまともだったんですが、

肝心の体験がメニューをあみだくじにするとか、おみくじを置くとか、スタッフ全員が違う挨拶をするとかなど、半分大喜利みたいな回答になってしまいました...!みなさんが優しい目で見守ってくださっていたので、なんとか乗り切れました。

アウトプットの質はともかく、モデルパターンや感情曲線など、ある程度ツールが整っていると、ものすごいスピーディにアイデアが出たことに驚きました。

あと、コントを元にしているからか、脳内で想像するカスタマージャーニーが映像で再現されたのが印象的でした。なので文章化しづらい視線の動きや普段はよくみていないちょっとした引っかかりなどにも気づくことができました。実験は大成功だと思います!

まとめ

私は「UX」という言葉が少し苦手でした。あまりにも漠然としていて、どのことを指しているのかわからず不安だったので、進んで使おうと思っていませんでした。

ですが今回このイベントに参加したことで、私の中の「UXデザインの解像度」は確実に上がったと感じています。

どのセッションも共通して感じたのは「ユーザーや、仕事でやりとりする相手に真摯によりそい、最善を届ける努力をすること」が大事であるということです。

難しいことはまだまだわからないですし、プロの方から見たら「何をあたりまえのことを」と思われるかもしれないのですが、これこそがUXデザインの本質なんじゃないかなぁ、と新人ながら自分なりの答えを見つけることができました。

今回得た知識を無駄にしないよう、自分のできることからコツコツとUXデザイン業務を頑張っていこうと思います!

さいごに

GameWithでは一緒に働く仲間を募集中です!

毎月もくもく会なども開催しているので、気軽にご連絡ください!

hrmos.co

UX MILK Fest 2019にスポンサーとして協賛します #uxmilk_fest #GameWith #TechWith

こんにちは。GameWithのエンジニアのtiwuです。

この度、UX MILK Fest 2019にスポンサーとして協賛することになりました!

uxmilkfest2019.studio.design

当日は来場者の方にノベルティが配布されるのですが、弊社からは社内デザイナーが制作したデザインマネージャー採用のお知らせチラシをお渡しいたします。チームが求める人材について熱く語っておりますので、ぜひ目を通していただけますと幸いです...

f:id:tiwu:20190913143156j:plain

当日はGameWithのデザイナーが複数名参加するので、一緒に楽しみましょう!

さいごに

GameWithでは一緒に働く仲間を募集中です!

毎月もくもく会なども開催しているので、気軽に連絡ください!

hrmos.co

GameWith フロントエンド もくもく会 #14 開催しました #GameWith #TechWith #gamewith_moku2

こんにちは。GameWithのエンジニアのtiwuです。

8月29日(木)にGameWith主催で第14回目のもくもく会を開催しました!

GameWith フロントエンド もくもく会 #14

gamewith.connpass.com

今回のテーマも前回に引き続き、僕自身が関心を高く持っているフロントエンドを採用しました。

今回は社内1人、社外7人の合計8人で開催しました!

f:id:tiwu:20190329134140p:plain

もくもく会は最初にお知らせ、次に自己紹介と今日のもくもく内容を発表して、もくもくし、最後に進捗発表という流れで開催しました!

2時間ほどもくもくしたら本日の進捗の発表をしました!

  • Firebase functions + Express + Web Componentsの開発
  • 新しいJSフレームワークの作成
  • enebularを利用したIoT開発
  • ReactのSSRでブログを作成
  • 「JavaScriptコードレシピ集: スグに使えるテクニック278」で勉強
  • PWAのpush通知の実装
  • 画像をフロントで圧縮しFirebaseにアップロード機能の実装
  • Vue.jsを利用したtodo listの実装

といった様々なテーマの取り組みのもくもく会でした!

新しいJSのフレームワークの作成は驚きました!完成が楽しみです!

次回告知

次回は9/26に開催します!次回のテーマはiOSです!

gamewith.connpass.com

最後に

参加していただいた皆様ありがとうございました!

GameWithのDeveloper向けTwitterアカウントを開設しました。

もくもく会の告知やブログの更新情報などを発信するので良かったらフォロー宜しくお願いします!

twitter.com

DIKitで人間がクラス間の依存関係を解決するのを終わらせる #GameWith #TechWith

こんにちは、つい最近入社したiOSエンジニアのみなみ(@yuuxeno)です。

DIKitとは、AndroidのDaggerの影響を受けて作られた、iOSアプリ開発(Swift)でも使える、コード生成型のDIライブラリです。コード生成でDIを実現する仕組みは他の言語にも存在し、例えばGo言語にはWireというツールがあります。

先日、potatotips #64において、DIKitについての発表(下のスライド参考)をしました。


今回は、 DIKitとは何なのか?コード生成でDIを実現するメリットとは?などについて、発表した時とは違う話の構成で、ブログ記事で紹介したいと思います。

はじめに

コンポーネント間の依存を解決し、そのコードを書く。ということを、ソフトウェアエンジニアは日常的に何度も行います

例えば、iOSであればViewControllerが単体で使えることは稀です。PresenterやViewModelなど、ViewControllerは大抵なんらかのクラスに依存しています。

// ViewControllerはViewModelに依存
let viewController = ViewController(viewModel: ViewModel())

ViewControllerだけでなく、ViewModel(もしくはPresenter)も大抵なんらかのクラスに依存しています。

// ViewControllerはViewModelに依存、ViewModelはModelに依存
let viewController = ViewController(viewModel: ViewModel(model: Model()))

ViewControllerが依存しているものを"目"で確認します。すると、ViewModelに依存してることが分かりました。今度は、ViewModelが何に依存しているかを確認します。どうやらViewModelはModelに依存しているようです、、、。この間、Xcodeのコード補完の助けを借りながらも、"手"で何度もキーボードをタイプします。この作業をひたすら繰り返します

ViewControllerを生成するというたった1つのことに対して、"人間"が"目"で何度も確認して、"手"でキーボードを何度もタイプしてコードを書く必要があります。

ViewControllerも、ViewModelも、Modelも、1つのアプリにそれぞれいくつもあります。この単純で退屈な作業を、人間が何度も繰り返しています。

人間が依存関係の解決をすることで起こること

DIできない

class ViewController {
    // 生成処理が直接書かれている
    let viewModel = ViewModel(model: Model())
}

ViewController内でViewModelを直接生成しています。ViewControllerに限らず、これは結構あるあるパターンだと思うのですが、何が問題なのでしょうか?

まず、ViewControllerを単体でテストするのが難しくなります。ViewModelをモック用のオブジェクトに差し替えることができないからです。

コードの見通しが悪い

また、ViewControllerにとって重要なのは、ViewModelが使えるかどうか、それだけです。ViewModelがどのように生成されるか(この例ならModelが必要とか)は、ViewControllerにとって知る必要のないことです。

そのクラスがどんな機能持つかと、そのクラスが依存しているオブジェクトの生成方法は、別の種類のものです。全く異なる種類の処理が1つのクラスにあまりに増えることは、コードをぱっと見で把握するのを難しくします。

依存関係を解決する処理を人間が管理する必要

protocol AppResolver {
    func resolveViewController() -> ViewController
}

final class AppResolverImpl: AppResolver {
    // ViewControllerの依存関係を解決する処理
    func resolveViewController() -> ViewController {
        return ViewController(viewModel: ViewModel(model: Model()))
    }
}

let viewController = appResolver.resolveViewController()

今度は、AppResolverという依存関係を管理する専用の場所を作りました。
AppResolver内で対象のクラスに依存性を注入できるようにし、依存関係を解決する処理をAppResolverに分離します。
ViewControllerは単体でテスト可能になりました。依存関係を解決する処理が分離されたことで、ViewControllerのコード自体も少しすっきりしたことでしょう。

これで色々な問題を解決することができました、、、本当にそうなのでしょうか?

依然としてViewControllerの依存関係は、"人間"が"目"で確認し、キーボードを"手"でタイプして解決する必要があります。

なにより、人間が書いたコードは、人間が管理する必要があります。ミスがある可能性があるからです。
例えばメソッド名などが規約通りなのかは、コードレビューで人間がチェックする必要があります(resolveViewControllerでなく、resolveVCになっていないかなど)。
また、引数名や引数自体が変更になった場合など、それぞれのコンポーネントが依存するものに変更があった場合、その修正も人間がする必要があります。

DIKitが可能にすること

DIできなかったり、依存関係を解決する処理が整理されていない、というのは良くあることです。

何故なのでしょうか?DIについての知識がたまたま無かった、かなり急いでいた、、、。理由は色々あるように思えますが、突き詰めれば、依存関係の解決を"人間"がするからではないでしょうか?

人間がするからミスが発生します、全ての人が全てのことに詳しいわけではありません、人によりコードに差が出ます、そしてこれらが色々なところで繰り返されます

解決策はないのでしょうか?

もしかすると、これから紹介するDIKitというライブラリが、1つの選択肢になるかもしれません。全ての問題を解決できるわけではないですが、依存関係の解決を人間がすることの煩わしさを、DIKitにより軽減できます。

DIKitとは?

DIKitとは、コード生成によりDIを実現するためのライブラリです。

DIKitライブラリには、コード生成を行うコマンドラインツールであるdikitgenが含まれています。dikitgenは依存関係を解決 & その処理をコード生成します。これにより、今まで人間が行なっていたクラス間の依存関係の解決を、部分的に自動化することができます。

DIKitは実際に使ってみて真価が分かる系のライブラリです。このライブラリを使うことにより、Xcodeの実行ボタンを押した時(dikitgenが実行された時)に何が起こるのか、ざっくり簡単に説明したいと思います。

ViewController, ViewModel, Modelの3つのクラスがあります。ViewControllerはViewModelに依存、ViewModelはModelに依存しています。
ここで、他のものに依存しているViewController, ViewModelについては、それぞれprotocol Injectableを実装します。
他のものに依存していないModelについては、AppResolver(AppResolverImpl)にその生成処理を追加します。

// ViewController
class ViewController: Injectable {
    struct Dependency {
        let viewModel: ViewModel
    }

    required init(dependency: Dependency) {...}
}

// ViewModel
class ViewModel: Injectable {
    struct Dependency {
        let model: Model
    }

    required init(dependency: Dependency) {...}
}

// Model
protocol AppResolver: Resolver {
    func provideModel() -> Model
}

final class AppResolverImpl: AppResolver {
    func provideModel() -> Model {
        return Model()
    }
}

これで準備は完了です。Xcodeの実行ボタンを押しましょう。

コード生成されるときの様子

f:id:yuuxeno:20190902124737g:plain

生成されたコード

extension AppResolver {

    func resolveModel() -> Model {
        return provideModel()
    }
    
    // ViewControllerの依存関係を解決する処理
    func resolveViewController() -> ViewController {
        let viewModel = resolveViewModel()
        return ViewController(dependency: .init(viewModel: viewModel))
    }

    // ViewModelの依存関係を解決する処理
    func resolveViewModel() -> ViewModel {
        let model = resolveModel()
        return ViewModel(dependency: .init(model: model))
    }

}

今まで手動で書いていた依存関係を解決する処理が、一瞬でコード生成されました。
これぐらいのサンプルコードであれば、手動で依存関係を解決するメリットがあまり感じられないかもしれません。

しかし、そこそこの規模のアプリで、今まで何行も手動で書いていた大量の処理が、一瞬でコード生成され、DIKitの管理下に入る。そんな光景を見ると、このライブラリの持つポテンシャルの高さを感じることができます。(ちなみに後述するのですが、依存関係を解決する全ての処理をコード生成できるわけではないので、その点は注意が必要です)

DIKitでできる

人間の代わりに依存関係を解決

依存関係の解決というのは単純なパズルです。必要なピースは全て決まっていて、あとはひたすらそれをコード全体から集めてくるだけです。その単調な作業をDIKitに任せることができます。

依存関係を解決するコードの管理

それぞれが数行の依存関係を解決する処理でも、プロジェクト全体だとすごい量になります。DIKitはその大量の依存関係を解決する処理を、人間の代わりに管理してくれます。人間が管理する処理を少しでも減らすことができるのは、大きなメリットです。

プロジェクト全体をDIできる仕組みの提供

今はプロジェクトの度に、アプリ全体としてどう依存関係を解決するのかを考える必要があります。依存関係を解決する専用の場所を作るのか、必要なところでそれぞれ依存を解決するのか。依存を解決する処理の規約をどうするのか、、、などです。

依存関係を解決する処理はとても普遍的なものです。星の数ほどあるアプリ、それぞれで独自の仕組みを1から考えるのは効率が良くない気がします。

DIKitは単に依存関係を自動で解決してくれるだけでなく、プロジェクト全体をDIできる仕組みも提供します。DIKitの規約に従いながら、依存関係を解決すれば、あなたのプロジェクトは自然とDIできるものになります

DIKitでできない

全ての依存関係を自動で解決することはできない

DIKitで自動(コード生成)で依存関係を解決できないパターンが何個かあります。

その1つが、protocolの実体クラスが、他のものに依存しているパターンです。
以下のコードでは、ViewControllerがprotocol ViewModelに依存し、そのprotocol ViewModelの実体クラスViewModelImplが、protocol APIClient(実体クラスAPIClientImpl)に依存しています。
ここでは、ViewControllerの依存関係を解決する処理はコード生成されます(resolveViewControllerメソッド)。対して、protocol ViewModelの実体クラスViewModelImplの依存関係を解決する処理は、コード生成自体はされるのですが、そのままでは使えません。この部分だけは、従来通り依存関係の解決を手動で行う必要があるのです。

// APIClient
protocol APIClient {
    ・・・
}

final class APICientImpl: APIClient {
    ・・・
}

// ViewModel
protocol ViewModel {

}

final class ViewModelImpl: ViewModel, Injectable {
    struct Dependency {
        let apiClient: APIClient
    }

    init(dependency: Dependency) {...}
}

// ViewController
final class ViewController: Injectable {
    struct Dependency {
        let viewModel: ViewModel
    }

    init(dependency: Dependency) {...}
}

final class AppResolverImpl: AppResolver {
    func provideAPIClient() -> APIClient {
        return APIClientImpl()
    }

    // ViewModel(ViewModelImpl)の依存関係を解決する処理は、以下のように手動でする必要
    func provideViewModel() -> ViewModel {
        // 一度dikitgenを実行すると、下のコードと同じようなViewModelImplの依存関係を解決する処理が生成されますが、  
    // それを下のコードと置き換えて使ったとしても、いずれにせよdikitgenだけで依存関係を解決することはできません。
        return ViewModelImpl(.init(apiClient: provideAPIClient()))
    }
}

// ViewControllerの依存関係を解決する処理は、以下のようにコード生成される
extension AppResolver {
    func resolveViewController() -> ViewController {
    let viewModel = provideViewModel()
    return ViewController(dependency: .init(viewModel: viewModel))
    }
}

もう1つがシングルトンです。
シングルトンについても、ここら辺のissueを参考に、手動で依存関係を解決する処理を追加する必要があります。

以上のように一部の依存関係については、従来通り人間が解決する必要があります。

ただ、DIKitなどのコード生成型のDIライブラリを使わない従来の方法では、人間が100%依存関係を手動で解決していたことを考えると、一部分でも自動化できるのは大きな前進です。

現状のGameWithアプリにおけるDI

GameWithアプリは現在手動でDIする処理を書いていたり、そもそもDIせず直接生成するコードが書かれていたりバラバラです。
将来的にはプロジェクト全体をDIできるようにするついでに、DIKitなどコード生成型のDIライブラリを入れたいと思っています!

DIKitが持つ大きな可能性

星の数ほどあるアプリ、その全ての依存関係を解決する処理が整理されて、DIもできるものだったら、、、
依存関係を解決する処理を整理したり、DIできるようにリファクタリングすることは不要になり、多くのコストが浮きます。見通しの良いコードがより効率的な開発を可能にします。DIについて考える時間は必要ありません、全ての仕組みはDIKitが用意してくれています

その結果、もっと別のこと、例えばアプリをもっと使いやすいものにする、などのことにリソースを注力できるようになります

そうなれば、もっともっとおもしろいアプリが出てくる、そんな未来がやってくる、そうあなたも思いませんか?

参考

最後に

GameWithのDeveloper向けTwitterアカウントを開設しました。

もくもく会の告知やブログの更新情報などを発信するので良かったらフォロー宜しくお願いします!

twitter.com