Slide 1

Slide 1 text

7年開発・運用が続いている kencomのiOSアプリの全面的リ ファクタリング 野瀬田 裕樹

Slide 2

Slide 2 text

野瀬田 裕樹 所属 ヘルスケア事業本部 製品開発部モバイルソリューションG 担当 iOS/Androidエンジニア 出身地 京都府 家族構成 妻と4歳の長男の3人家族 2 @ynoseda

Slide 3

Slide 3 text

kencomとは ● https://kencom.jp/ ● 2015年10月にv1.0.0をリリース ● 楽しみながら健康になれることを促す ● 健康保険組合・自治体などの団体に対 して販売を行っている ○ 団体に所属している人だけが使える ● 健康診断の結果の閲覧、健康状態に応 じた記事のレコメンド、歩数データの 管理などができる 3

Slide 4

Slide 4 text

● 1st commit:2015年6月5日 kencom iOSアプリの開発の履歴 4

Slide 5

Slide 5 text

● 1st commit:2015年6月5日 ● 1st release:2015年10月8日 =開発期間4ヶ月 kencom iOSアプリの開発の履歴 5

Slide 6

Slide 6 text

● 1st commit:2015年6月5日 ● 1st release:2015年10月8日 ● 基本的には100% swift kencom iOSアプリの開発の履歴 6

Slide 7

Slide 7 text

● 1st commit:2015年6月5日 ● 1st release:2015年10月8日 ● 基本的には100% swift ● 短期間での開発のためか初期から負債が蓄積された状態 ○ Realmを使っているが不安定で壊れたりしていた(その後に削除) ○ FatViewController、FatStoryboard ○ ViewController内でviewの位置調整などを多数やっている kencom iOSアプリの開発の履歴 7

Slide 8

Slide 8 text

● 現時点で24 Contributors ● gitのログから大規模なアプリの機能改修は今まであまりなく、 リファクタを推進するきっかけに乏しかった様子が伺える kencom iOSアプリの開発の履歴 8

Slide 9

Slide 9 text

● 現時点で24 Contributors ● 大規模なアプリの機能改修は今まであまりなかった ● その結果、秘伝のタレコードが多数発生 ● 以下のような課題が発生 ○ 提供する機能要件に対して不必要に多いコード量 ○ 現在では不要になった多数のOSSのライブラリ ○ 結果としてビルド時間が長くなってきた kencom iOSアプリの開発の履歴 9

Slide 10

Slide 10 text

● 色々な負債が存在していたため、全てをリファクタは困難 リファクタリングの方針 10 秘伝のタレ 重いビルド 古い設計 不要な実装 古い書き方

Slide 11

Slide 11 text

● 色々な負債が存在していたため、全てをリファクタは困難 ● そこでまずは今後の開発を楽にするためのリファクタを実施 ● リファクタリングの方針 ○ 通信部分の刷新 → 今後の機能開発で簡潔に書けるようになる ○ プロジェクト全体のスリム化 → 断捨離でビルド時間短縮を期待 リファクタリングの方針 11

Slide 12

Slide 12 text

● 通信ライブラリの選定 ● インターフェース設計 ● 通信呼び出し部分の差し替え作業 通信部分の刷新 12

Slide 13

Slide 13 text

● 旧実装ではライブラリとしてMoyaを採用 ● MoyaとはAlamofireをラップしたライブラリ 通信部分の刷新:通信ライブラリの選定 13

Slide 14

Slide 14 text

● 旧実装ではライブラリとしてMoyaを採用 ● MoyaとはAlamofireをラップした通信用のライブラリ ● しかし、旧実装ではMoyaのメリットが活かせていない状態 ○ 通信部分の単体テストは行ってない ○ Moyaを更にラップした通信クラスがある 通信部分の刷新:通信ライブラリの選定 14

Slide 15

Slide 15 text

● 考えられる方針 ○ ライブラリは今のまま:Moyaのメリットを最大限に生かす ○ ライブラリを使わない:URLSessionを直接使用する ○ 使うライブラリを変更する:Alamofireに切り替える 通信部分の刷新:通信ライブラリの選定 15

Slide 16

Slide 16 text

● 考えられる方針 ○ ライブラリは今のまま:Moyaのメリットを最大限に生かす ○ ライブラリを使わない:URLSessionを直接使用する ○ 使うライブラリを変更する:Alamofireに切り替える 通信部分の刷新:通信ライブラリの選定 16

Slide 17

Slide 17 text

● 旧実装のインターフェースはRxSwiftのObservable ● 具体的には、request関数の返り値がObservable 通信部分の刷新:通信のインターフェース 17

Slide 18

Slide 18 text

● 昔はRxSwiftを利用したReactiveプログラミングが多かった ● Swift 5.5からasync/awaitを使った非同期処理が可能になった ● 同期処理のコードにasync/awaitのキーワードを追加するだけで 非同期処理になるので非常にシンプルに書ける 補足)Swiftの非同期処理について 18

Slide 19

Slide 19 text

通信部分の刷新:実装イメージ 19 ● 通信を行うstaticな関数を準備 ● 新しい実装ではasyncな関数として通信のI/Fを用意

Slide 20

Slide 20 text

通信部分の刷新:実装イメージ 20 ● APIのリクエストに必要なものを実装するためのprotocolを準備

Slide 21

Slide 21 text

● 作業手順 ○ 全てのAPIを一覧にしてチェックリストを準備 ○ API一つにつきAPIRequestに適合したstructを準備 ○ 一つずつ新しいrequest関数に置き換え ○ 全て置き換えが完了したら古いコードを削除 通信部分の刷新 21

Slide 22

Slide 22 text

● 作業手順 ○ 全てのAPIを一覧にしてチェックリストを準備 ○ API一つにつきAPIRequestに適合したstructを準備 ○ 一つずつ新しいrequest関数に置き換え ○ 全て置き換えが完了したら古いコードを削除 通信部分の刷新 22

Slide 23

Slide 23 text

● 利用しているライブラリの見直しを実施 ○ 元々使用していたライブラリの数:25個 ○ 断捨離できるライブラリの数:半分以上 ● 元々使っていたライブラリの例 ○ Rx系(RxSwift, RxCocoa) ○ ObjectMapper ○ CryptoSwift プロジェクト全体のスリム化 23

Slide 24

Slide 24 text

● Rx系の断捨離の方針案 a. 対応するCombineのコードで置き換え b. 個別に最適な実装にリファクタ プロジェクト全体のスリム化:Rx系の断捨離 24

Slide 25

Slide 25 text

● Rx系の断捨離の方針案 a. 対応するCombineのコードで置き換え b. 個別に最適な実装にリファクタ c. 一部はリファクタするが、大半は機械的にCombineに置き換え プロジェクト全体のスリム化:Rx系の断捨離 25

Slide 26

Slide 26 text

● Rx系の断捨離の対応内容 ○ ほぼ機械的にCombineのコードに置換できたため、ひたすら置換 ○ 通信部分やRepositoryへのアクセスはasync/awaitで書き換え ○ RxCocoaなど機械的な置換ができない箇所だけ後から手動書き換え ○ 基本的に地道な作業なので気合いで修正を完了 プロジェクト全体のスリム化:Rx系の断捨離 26

Slide 27

Slide 27 text

● ObjectMapperについて ○ Jsonと任意のモデルオブジェクトとを相互に変換できる ○ 今ではCodableがある ○ 通信部分の刷新に合わせて全てCodableに置き換え ○ これもほとんど機械的な修正で済むため、気合いで修正 プロジェクト全体のスリム化:ObjectMapperの断捨離 27

Slide 28

Slide 28 text

● CryptoSwiftについて ○ 暗号化/復号化のためのライブラリ ○ 標準のCryptoKitに置き換えできる ○ 通信のJWTを生成するためだけに使用していたので、すぐに修正完了 プロジェクト全体のスリム化:CryptoSwiftの断捨離 28

Slide 29

Slide 29 text

● その他の断捨離について(一部) ○ SwiftDate → あまり使ってなかったので標準のDateの実装に書き換え ○ Appirater → レビューのタイミング調整にだけ使ってたので削除 ○ XCGLogger → ほぼ使ってなかったので削除 ○ PKHUD → 自前のローディング画面に置き換え ○ Device → 更新されてなかったので削除 ○ SwiftyDrop → 自前のViewに書き換え プロジェクト全体のスリム化 29

Slide 30

Slide 30 text

● 10個以上のライブラリを断捨離 ● 該当バージョンで3万行以上コードを削減 リファクタリングの結果 30

Slide 31

Slide 31 text

● ビルド時間は5分以上かかっていたのが1分強に リファクタリングの結果 31

Slide 32

Slide 32 text

● QA検知の不具合は計7件 ● リリース後も大きな問題なく通常運用へ ● 要因 ○ 開発者側での手厚いレビューと開発者検証 ○ 短期間で集中的にリファクタを進めた ○ 機能開発とほぼ並行しないようにした ○ 十分に影響範囲をQAに伝えた リファクタリングの結果 32

Slide 33

Slide 33 text

● 継続して画面単位での改善も実施中 ○ 古い設計を新しい設計へ移行 ○ UIKitからSwiftUIへ移行 ● 同バージョンでホーム画面の作り直しも実施 その他のリファクタリング 33

Slide 34

Slide 34 text

● 数字ありバッジがついた丸型アイコンの実装 ● 単純にoverlayで重ねても、それっぽく見える 余談)SwiftUIで作ったViewを一部紹介 34

Slide 35

Slide 35 text

● 数字ありバッジがついた丸型アイコンの実装 ● 単純にoverlayで重ねても、それっぽく見える ● しかし、文字サイズを大きくすると位置がずれる ○ バッジの位置調整 or アイコンの可変化が必要 余談)SwiftUIで作ったViewを一部紹介 35

Slide 36

Slide 36 text

● 数字ありバッジがついた丸型アイコンの実装 ● overlayで重ねたバッジにoffsetを追加して調整 ● 指定するオフセットはバッジのサイズに応じて可変 ● 計算式:中学生ぐらいの気分になって考えてみよう 余談)SwiftUIで作ったViewを一部紹介 36

Slide 37

Slide 37 text

● 数字ありバッジがついた丸型アイコンの実装 ● overlayで重ねたバッジにoffsetを追加して調整 ● 指定するオフセットはバッジのサイズに応じて可変 ● 計算式:バッジの円の半径 - (1-1/√2) × アイコンの円の半径 余談)SwiftUIで作ったViewを一部紹介 37

Slide 38

Slide 38 text

● リファクタリングは地道な作業なので気合いは必要 ● 目的と手段を整理し、しっかり下準備すれば成功する ● リファクタリングの際には開発者確認とQAをきちんとしよう ● ちゃんとテストすれば、案外根幹部分に手を入れても大丈夫 まとめ 38

Slide 39

Slide 39 text

ご清聴ありがとうございました 良いリファクタリングライフを! 39