アルバイト先のiOSアプリをリファクタリングした


アルバイト先の会社で、プロジェクトの整備、リファクタリングをしようということになりました。

アプリのプロジェクトはこれまでオフショアで海外に渡ったり、業務委託やアルバイトに渡ったりと、カオス状態でした。

今回は、まとまった時間を設けてこのレガシープロジェクトを少しでも改善しようということだったので、最近学んだ内容をアウトプットするいい機会だと思って色々やってみました。

「これまでそんな事もやってなかったのか」と思われるかもしれませんが、
誤りや改善点等ありましたら、ご指摘いただければ幸いです。

### GitLabからGitHubへの移行

これまでGitLabで管理されていたプロジェクトをGitHubに移行することになりました。

私はREADMEやWikiの整備を行いました。

具体的には以下を含めました。

##### README
- 開発環境
- ライブラリ管理
- セットアップ方法
- API仕様書

##### Wiki
- 簡単なディレクトリ構成
- 主な使用ライブラリ
- Gitやコードの命名規則

また、ブランチの運用も変更しました。

- Main(リリース用)
- Staging(テスト用)

1. Mainからブランチを切って、作業用ブランチはStagingにマージ
2. Stagingでテスト完了後にリリースバージョンのタグをつけてマージ
3. Mainをリリース


### rbenv で ruby のバージョンを指定

CocoaPods は ruby で作られたツールです。

チーム開発を行う場合、各々の PC の ruby のバージョンが異なるとライブラリのインストールに失敗したり、上手く動かないことがあるので rbenv を使って ruby のバージョンを統一しました。

bundler など他にもツールが出てきて一気に理解するのは大変でしたが、以下の記事では丁寧に解説されています。

[Cocoapods と Carthage を共存させた iOS プロジェクトの始め方](https://qiita.com/yukin01/items/9d72154aead54bfd98cc)

### SwiftLint の導入

SwiftLintを導入しました。

ルールに関しては、業務委託の方が普段使用されている.swiftlint.yml ファイルが導入されました。

それに少しルールを追加しただけで、かなり緩めにルールが設定されています。

下記の記事を参考にさせていただきました。

[Swiftの静的解析ツール「SwiftLint」のセットアップ方法](https://qiita.com/uhooi/items/bf888b53b4b8210108aa)

### IQKeyboardManager の導入

これに関しては、本当に入れるだけで障壁はほぼありませんでした。

[IQKeyboardManager](https://github.com/hackiftekhar/IQKeyboardManager)

### リソース管理、R.swiftの導入

他言語対応されているはずなのに、NSLocalizedString が使用されずにハードコーディングされている箇所があったりと、こちらもカオス状態でした。

この機会なのでリソース管理のライブラリを入れようということで、導入がSwiftGenより簡単なR.swiftを導入しました。

注意点としては、生成された R.generated ファイルが、SwiftLint の制約エラーを起こすことがあるので、R.generatedファイルは .swiftlint.yml ファイルの excluded: に含める必要があります。

また、“キーワードが被っているという問題で、生成をスキップします“というような警告が出たので、リソース側の整理が必要でした。

現在は文字列しかほぼ書き換えられてないので、今後は画像、ストーリーボード、セルなども書き換える必要があります。

下記の記事を参考にさせていただきました。

[R.swiftのセットアップ方法(Swift5)](https://qiita.com/uhooi/items/82fbdd94bdc467a22422)

リテラルが結構使用されていたのでこちらも。

[R.Swiftでは文字リソースで「%@」を使うと、その部分を引数にとる関数を自動生成する]
(https://qiita.com/yosshi4486/items/1319d0cd87cc33326136)

### 継承が想定されないクラスには final をつける

Swift ではクラスを継承することができますが、自作のクラスで他のクラスから継承しないものには final をつけるべきです。

自分が継承されない想定で作ったクラスを他の人が継承する実装にしてしまったというようなことがチーム開発ではあり得るので、それを防ぐためには final を適切に設定することが必要です。 



### アクセスレベルを適切に設定

変数やメソッドなどには private などのアクセスレベルを設定することができます。 例えばクラス内でのみ使用し、クラスの外から呼ばれることのないメソッドは private にすることができます。 

final と同様に、アクセスレベルも適切に設定することが必要です。 

他人がプロジェクトのコードを読むときに「このメソッドは private だから外から呼ばれることはないんだ」と判断できるので、迅速な理解に繋がるというメリットもあると思います。

下記の記事で、非常にわかりやすく説明されています。

[アクセス修飾子を理解する](https://dev.classmethod.jp/references/ios8-swift-access-control/)

### delegate メソッドを extension に分ける

ViewController の定義時に全ての Delegate を継承せず適切に extension で分けました。

主に UITableViewDelegate や UICollectionViewDelegate
 、 UITextFieldDelegate のメソッドがそのままクラス内に書かれており、定義されているメソッドが delegate メソッドなのか独自のメソッドなのか分かりづらい状態でした。

こちらは自分で簡単にまとめたものを参考にしました。

[extensionを用いて可読性を上げる](https://qiita.com/jnosan_/items/918ba560ba528a98487a)

### 不要なコメントアウト、 print デバッグの削除

使用されていないコードのコメントアウトや、 print 文がそのまま残されている状態でした。

print 文の中で可能なものは、下記の記事を参考に os.Logger で置き換えられました。

[忙しい人向けの Explore logging in Swift - #WWDC20](https://note.com/tobi462/n/nc668bbc961b9)

例: 

```swift
let logger = Logger()

// 省略

do {
    // 処理
} catch {
    logger.error(error)
}

```

### 今後やりたいこと

#### 可能なところは StackView を使用

UI のメンテナンスの際は、強力な StackView である方がメンテナンスがやりやすいので、
可能なところは StackView を使用したいです。

#### 可能なところStoryboard Reference を使用

Storyboard が入り組んでいると、メンテナンスがやりづらいと思います。

特にチーム開発ではコンフリクトの原因にもなるので、可能なところは StoryboardReference で Storyboard を分けるべきだと思います。

#### SwiftFormat の導入

機械的にコーディングルールを適用させれば楽になるかと思います。

#### ネストの解消

可読性の低いネストが散見されるので、可能な限り guard 文などで可読性を改善したいです。



### まとめ

本来であれば、重複した処理を共通化したり、クラスの抽象化をしたりと、より高度なことをやりたいですが、現状の私のレベルで出来ることはこのくらいでした。

やるべき事は山積みですが、引き続きプロジェクトに貢献していきたいです。