🐧

何者でもなかったコードを何者かにしたい

2024/02/28に公開

自己紹介

こんにちは。助太刀アプリチームの中山です。

私は主に、助太刀iOSアプリ開発のテックリードを務めております。

助太刀iOSアプリにおける、リアーキテクチャ・リファクタリングに関して取り組んでいることを、この記事を通じて共有しようと思います。

今回は

何者かが決まると道は開ける

について話を進めていきたいと思います。

目的地を明確に定め、チームの方針が決まることで、おのずと開発効率やコード品質の向上が見込まれますね。

iOSアプリは最初、何者でもなかった?

私が助太刀に参画して最初に言われたのが、「何者でもないコードを何者かにしてほしい」でした。
MVVMっぽいファイルは存在するものの、View層にロジックがふんだんに盛り込まれている、いわゆるファットViewController状態でした。
またRxSwiftで書いてあるファイルが2,3あると思えば、delegateやNotification、クロージャーなどが混在していました。
新しく実装する時もその時々で変わっていました。

そして私が参画する直前まではRxSwiftを廃止しようという流れになっていたそうです。

コーディングの方針を決定する

既存コードはリリース済みであり、すでにテストを通っていて品質を担保されているコードであるのは間違いない。
なので、新規作成していく際にチームでコードの統一性を図ることから始めました。
コードの統一性を図るにあたって、「アーキテクチャはMVVMでいくのか?」、「RxSwiftは廃止するのか?」をまず決める必要があり、
そこから話していくことになりました。

アーキテクチャはMVVMでいくのか?

Clean Architecture、MVP、VIPERなどが候補として上がりましたが、
既存コードは役割がめちゃくちゃだとしてもMVVMとして書かれてる部分が多かったので、アーキテクチャはMVVMとしました。
そしてMVVMだけだとよくありがちなのが、ファットModelに陥ることです。
ファットModelになることで依存関係が密接になり、変更に強いコードとは言えないので、
下記の図のような構造を取ることにしました。
アーキテクチャーマップ

RxSwiftは廃止するのか?

世の中的にもよく言われている、RxSwiftは「勉強コストが高い」ことにおいては、もれなく助太刀iOSチームにも当てはまりました。
チームで方針決めミーティングを開き、メリットデメリットを簡単に出してみました。

▼メリット
・同時に更新する部分はRxの方が良い
・慣れるとコードが追いやすい
・サブスレッド上で動くためUX向上が期待できる
・コードが短くなって複雑度が下がる(可読性の向上が期待)

▼デメリット
・Rxを導入することで調査に時間がかかる
・慣れていないとコードが追いにくい(デリゲートの方が見やすい)
・デバックしにくい
・処理が冗長

ここでのデメリットは、慣れていないことが一番の原因であり、それが解決さえすればメリットの方が大きいとわかったので、RxSwift勉強会を開き、チームの基準の底上げを図ることにしました。
RxSwiftの概念であるストリームの考え方から、一つサンプルでRxSwiftでコーディングして、それを元に説明をすることで、概念が理解できれば案外簡単かも、となりました。
1番の懸念であった調査に時間がかかる件に関しては、

  • 勉強会の実施
  • 最初のうちは初動が遅れないように難しいRx記法は使用しない
  • そもそも何者かに決まっていないことが遅れる原因

上記をルールとして定め、RxSwiftの使用が確定されました㊗️

RxSwiftの使用前後の具体的なBefore/Afterを下記に記します。

Before

private let viewModel = 〇〇ViewModel() 

@IBOutlet private weak var 〇〇Label: UILabel!

override func viewDidLoad() {
    super.viewDidLoad()
    get〇〇()
}

private func get〇〇() {
    viewModel.get〇〇 { user in
        case .success:
            /* 表示するパーツが増えたり後続処理の機能が多ければここの責務が膨大になる */
            viewModel.user = user
            let name = viewModel.getName() 
            self.〇〇Label.text = name
            doSomethingAfterFetch()
        case .failure:
            // 何かエラー処理
    }
}

After

private let disposeBag = DisposeBag()
private let viewModel = 〇〇ViewModel() 

@IBOutlet private weak var 〇〇Label: UILabel!

override func viewDidLoad() {
    super.viewDidLoad()
    bind()
    viewModel.get〇〇() ←これを呼ぶだけ。VCget〇〇関数はいらない。
}

private func bind() {
    /* ViewはUI操作だけに専念できる */
    viewModel.userName 
        .bind(to: 〇〇label.rx.text)
        .disposed(by: disposeBag)
}

最後に

人は目的地が見えないで走り出すと、メンタル的にも迷いが生まれ、余計に体力を使い、思った以上のパフォーマスを発揮できません。
チームの方針が決定したことで、この後「コーディング規約」、「助太刀実装方針」、「リアーキテクチャ方針」など様々な仕組み化(ルール化)が行われ、大分コードの統一性を図れるようになってきました。
まだ方針が決定しただけで中身はスパゲティ状態なのは変わりません。
これからが本当の勝負なので、その辺の実施したことを記事にできればと思います。
最後まで読んでいただきありがとうございました。

助太刀では一緒に開発してくれるメンバを募集してます!
少しでもご興味を持っていただけたら下記よりお気軽にご連絡ください!

助太刀テックブログ

Discussion