【part 5】AppApp のリファクタリング過程紹介【ディレクトリ構成編】

どうも。Reoです。

AppAppリファクタリングシリーズも part 5 まで来ました。誰かこのシリーズ追ってくれる人いるのかもわからんですが、自己満足に書いていこうと思います。

part 4 を書いている時にひとつも作業できてないことに気づいたので、今回はちゃんと作業しながら書きます。

 

環境

  • Xcode11.3.1
  • Swift 5.1.3
  • iOS13.3
  1.  AppApp のリファクタリングを始めます!【SwiftLint 導入編】
  2. 【part 2】AppApp のリファクタリング過程紹介【SwiftLint導入後のError解消編】
  3. 【part 3】AppApp のリファクタリング過程紹介【SwiftLint導入後のWarning解消編】
  4. 【part 4】AppApp のリファクタリング過程紹介【コーディング規約編】

 

リポジトリ→ uruly/AppApp

 

今回の目標

ディレクトリを整理します。

きちゃないので。

今回はさくっと終わらせたい!

仕事で使っているのを気に入って真似して使っているので、ちょっとアレだったらごめんなさい。

 

整理前

晒すの怖い。

Xcodeでみたとき。(まぁ一緒なんですけども)

多分プロジェクト作成当時、ほんの少し悩んでViewごとに管理するようにしたような気がします。

 

Resource と Script と Storyboard を分けよう

どのアーキテクチャを利用するかによってディレクトリ構成は変わってくると思います。今回はCocoaMVCだと仮定しておきます。中身全然違うけど。

さてさて。まずは、大枠を作っていきます。

AppApp以下にResourcesとScriptsとStoryboardsフォルダを作ってそれぞれに振り分けます。

AppApp
├── Resources
├── Scripts
└── Storyboards

 

Storyboards

Storyboardをぶち込みます。

AppApp
├── Resources
├── Scripts
└── Storyboards
    ├── Base.lproj
    └── ja.lproj

うむ。

Resources

素材等をぶち込みます。

AppApp
├── Resources
│   ├── AppApp.entitlements
│   ├── Assets.xcassets
│   ├── GoogleService-Info.plist
│   ├── Images
│   ├── Info.plist
│   └── JSON
├── Scripts
└── Storyboards

Resourcesの中をどれだけ細分化するかはあとはよしなにって感じですね。Imagesフォルダには次回ぐらいに消えてもらいます。

 

Info.plist 等の場所を変更したら…

Info.plist の場所を変更したのでエラーが出ました。

Build input file cannot be found: ‘XXX/AppApp/AppApp/Info.plist’

なので以下の画像の箇所 TARGETS AppApp > Build Settings > Packaging > Info.plist File のパスを変更してあげます。

AppApp/Resources/Info.plist に変更してあげました。

 

同様に、AppApp.entitlements の場所を変更したので以下のエラーが出ました。

The file “entitlements” could not be opened. Verify the value of the CODE_SIGN_ENTITLEMENTS build setting for target “AppApp” is correct and that the file exists on disk.

以下のCode Signing Entitlements のパスを変更します。

AppApp/Resources/AppApp.entitlements に変更しました。

 

Scripts内を整理する

あとはScripts内の整理をしていきます。

くっそー型に当てはまらない何なんだこのプロジェクトは…

元々のAppAppのフォルダ整理の仕方は、画面があって、その画面で使われているファイルをまとめる方式だったようですね。

あと、一番辛いのが、FatViewController を避けるために UICollectionView 等のサブクラスを作ってViewControllerで配置ってのをしてるんですね…。なので何かのサブクラスがめっちゃいっぱいある。つらい。

 

整理してきました。

AppApp
├── Resources
├── Scripts
│   ├── AppDelegate.swift
│   ├── Common
│   ├── Components
│   ├── Entity
│   ├── Extensions
│   ├── Model
│   └── UIViewController
└── Storyboards

AppDelegate.swift 以外はフォルダにぶち込みました。

スッキリしたかな…?

Cocoa MVC ですらないであろうこのプロジェクトですが、大体OKAIMOたんと同じ構成になりました。無理矢理しました。

 

各フォルダを見ていきましょう。

 

Common

中身。

Scripts
├── Common
│   ├── Constants.swift
│   ├── GATrackingManager.swift
│   └── VersionManager.swift

あんまりManagerって名前のものを作りたくないお気持ち。

GATrackingManager も VersionManager も消えてもらっていいファイルなので後々消えます。

定数などをまとめたConstants.swiftを置いています。

 

Components

UIViewController 以外のUIKit系のサブクラスを配置しています。

Scripts
├── Components
│   ├── UIActivityIndicatorView
│   ├── UICollectionView
│   ├── UICollectionViewCell
│   ├── UIImageView
│   ├── UINavigationBar
│   ├── UIPickerView
│   ├── UITableView
│   ├── UITableViewCell
│   ├── UIToolbar
│   └── UIView

各パーツごとにそれぞれフォルダを用意してます。

AppAppのヤバさは、UICollectionViewとかUITableViewとかUIViewにわさわさとファイルがあることです。やばいです。


│   ├── UICollectionViewCell
│   │   ├── App
│   │   │   ├── AppCollectionViewCell.swift
│   │   │   └── AppCollectionViewCell.xib

UICollectionViewCellの中身はこんな感じで、さらにフォルダに入れています。

まじで、名付けがやばい。

名前変えるところからやらないといけない気もするけれど、そもそもUICollectionViewのカスタムクラスとかは消えて欲しいので、色々と厳しい。

 

Entity

CodableとかRealmのObjectとかそういうのを置いています。DBのEntityですね。

Scripts
├── Entity
│   ├── AppLabel.swift
│   └── ApplicationData.swift

この名付けも設計も酷すぎてつらぽよ。Codableが存在しない世界線だった。

 

Extensions

Extensionを置いています。

Scripts
├── Extensions
│   ├── Array+FindIndex.swift
│   ├── UIApplication+Stack.swift
│   ├── UIColor+AppColor.swift
│   ├── UIColor+Compare.swift
│   ├── UIImage+MaskCorner.swift
│   ├── UIResponder+FindView.swift
│   ├── UITextView+Placeholder.swift
│   └── UIView+SnapShot.swift

いらないExtensionが多そう…。

昔と今と唯一変わらないのはExtensionのファイル名かもしれない…。

 

Model

MVCのModelにあたる部分ですね!File is empty! Why〜!?

 

UIViewController

UIViewControllerを配置しています。

Scripts
└── UIViewController
    ├── AppList
    ├── Base
    ├── CreateAppLabel
    ├── Detail
    ├── EditAppLabel
    ├── LabelList
    ├── SetInfo
    ├── Tutorial
    └── Web

MVVMでやってる時はここの構成を以下のようにしてました。

Scripts
└── View
    ├── AppList
    │   ├── Router
    │   ├── ViewController
    │   └── ViewModel

 

名付け下手だー。今も下手だけど昔は3倍ぐらい下手だー。

 

まとめ

意外とこういうディレクトリ構成の話とかって、社内やプロジェクト内では定められているのかもしれないけれど、全然表に出てこない気がします。

独学で1人で篭ってやっていると、なかなかこうした知識を得ることが難しいなぁとも思います。

 

今回紹介したこの構成もベストと言い切れるかはわからないですし、もっと良い方法があるかもしれません。

例えば、MicroViewControllerの考え方で設計しようと思ったら、ComponentsとなるUIViewControllerと、ScreenとなるUIViewController が存在するはずなので、今のUIViewControllerフォルダではちょっとイマイチかなーとも思います。

 

でも今回に関しては、整理する前よりは良いはず。断然見やすい…はず…。

GitHubで他の人のプロジェクトとかもみるけれど、今の所この整理の仕方が一番好きになれています。

ディレクトリ構成を一貫してやれば複数のプロジェクトを運営していても迷うことが減るはずです。

 

今回使った tree コマンド

tree コマンド便利でした。階層表示してくれるよ。brew で入れられるよ。

treeコマンド – Qiita

 

次回

R.swift を入れましょう!あとは画像ファイルと色の管理を変えていきます。

今日もう一本書くのはきついかなーーーーー。

 

おわりに

最近はこんな感じの構成でやっています。っていうかもうパクリなのでパクリって言われないか怖い。でも良いものは…広めていきたいじゃないか…?許可を…とれ…?

とりあえず、Modelにあたるファイルがないってやばいなって思いました。でもiOS開発でModelにあたるファイルって意外と難しい気もしてます。

あとはAPI等も使っていないので、その辺のフォルダもないですね。

 

そういえば、昔のXcodeのフォルダには青色と黄色の違いがあったけれど気がついたら消えてましたね。

 

いやー書き始めたは良いけど、先が長すぎる。満足する日なんて来ないかもしれないです。

働く前のコードを見ると、自分が今どうして首切られてないのか疑問に思えてきますね。多分嫌われないように相当頑張ったんだろうな…。これからも頑張ろう…。

 

今回はサクッと書きたいと言ってもうすぐ5000文字なので書きすぎマンですね。誰が読むんだ。

現実から逃避しすぎてブログ書くのが捗りまくってしまう。

ではでは。

 

AppApp ,
コメントは認証制です。詳しくは下記の注意をお読みください。お気軽にコメントお願いします!

Write a Comment

コメント時の注意

「Twitter」「Facebook」「Google+」「WordPress」のいずれかのアカウントをお持ちの方は各アカウントと連携することでコメントできます。 コメントしたことはSNSに流れませんので、アカウントをお持ちの方はこちらの方法でコメントを投稿して下さると嬉しいです。 アカウントをお持ちでない方はメールアドレスで投稿することができます。 初回コメント時は承認後に表示されます。

Related Memo...

UITableView.RowAnimation の .none はアニメーションするよ

UITableView.RowAnimation の .none はアニメーションがnoneなわけじゃなく、デフォルトの設定を使うよという意味らしい。

The inserted or deleted rows use the default animations.

なのでアニメーションしちゃう。今更の気づき。

 

iOS

UINavigationController + UIScrollView の組み合わせで使っている時に謎の余白ができる時

UINavigationController + UIScrollView の組み合わせで使っていて、UIScrollView 上に AutoLayout で上下左右0で View を設置しているのに、30px程度上にずれてしまうとき。

`navigationController.navigationBar.isTranslucent = false` にすると直るかもしれない。

ScrollView上のコンテンツとNavigationBarの重なっているところが透過していたら多分これで直せるはず。

通常のターゲットではちゃんと動いているのに、iOSSnapshotTestCase を用いたテストでだけこの対応が必要なのよくわからないけれど。。。

iOS

記事を書くほどでもないけれどメモっておきたいこと

テスト投稿。

例えばiphone7 の画面サイズ

750 × 1334
半分375 × 667

iOS
more