アプリ開発時のファイルツリーのベストプラクティス
matome_filetree
スポンサーリンク

iOS/Androidのディレクトリ配置のベストプラクティス

Xcodeを用いたiOS(Swift)で、MVVMモデルの例で紹介します。

ナビゲーターエリアのファイルツリーのベストプラクティス

ヒーラー
何がスタンダードなのかよく分からないで開発を進めてる

はじめに

Xcodeを開いて左側にあるナビゲーターエリア。
ここに表示されるファイルツリーのディレクトリとファイルの構成を最適にしたいですよね。
新規開発のときには特に、その後の基準となるため重要です。
既存プロジェクトやサンプルを見たりして、結果的に収まりが良くなっていたりすると思いますが、改めて、学習サイトを通じてスタンダードと思える形を紹介します。

尚、Xcodeを用いたiOS(Swift)で、MVVMモデルの例で説明しますが、Androidにも通ずるところは多くあると思います。

ディレクトリ構成

ファイルツリー

MVVMモデルとして大枠のディレクトリはこうなります。

  • Model
  • View
  • Controller
  • ViewModel
  • API
  • Utils
Modelディレクトリ
Model

モデルを配置します。
データの格納庫となる構造体が主に当てはまります。
ビューとの関係付けは、ビューモデルが担当します。

Viewディレクトリ
View

UI表示に関わるビューを配置します。
画面に関わる部分は、ViewControllerとして、コントローラーが担当しますが、ここでは、パーツ単位でのビュークラスを管理します。

上の例では、UITableViewCellのセルやヘッダー、UITextFieldなどの標準UIパーツをカスタマイズしたUIなどを管理しています。
尚、プロフィール画面に関わるものはProfileディレクトリにグループ化しています。
画面単位でグルーピングすると分かりやすくなるでしょう。

Controllerディレクトリ
Controller

コントローラー(ViewController)を配置します。

上の例では、メイン画面、プロフィール画面、ログイン画面、登録画面とあり、認証関連のログイン画面と登録画面は、Authenticationというディレクトリでグループ化しています。

ViewModelディレクトリ
ViewModel

ビューとモデルの橋渡し的存在のビューモデルを配置します。

上の例では、認証関連のログイン画面と登録画面の両方におけるビューモデルで、AuthenticationViewModelとして管理しています。
一つのViewModelファイルに統一している理由は、共通する持ち物があるためで、内部では、AuthenticationViewModelプロトコルを定義し、LoginViewModelとRegistrationViewModelに分けて、それぞれがAuthenticationViewModelを継承する形にしています。
それぞれのViewModelが肥大化するようであれば、ファイルも分割した方が良いかもしれません。

APIディレクトリ
API

APIに関するファイルを配置します。
シンプルなのは、クエリとレスポンスを受けるためのクロージャーを渡すstaticメソッドで、リクエストと通信を経てのレスポンス(エラー含め)を返す振る舞いを持ったイメージですね。

上の例では、認証に関わるAPIとユーザー情報を取得するAPIをサービスをそれぞれAuthenticationService、UserServiceとして作成しています。

Utilsディレクトリ

ユーティリティファイルを配置します。

機能拡張(extension)や共通的に使われるものを中心に定義します。
他のプロジェクトでも使い回せることをイメージすると良いでしょう。

ここでは、以下のファイルを定義しています。

  • Extensions.swift
  • Constants.swift

Extensions.swiftでは、既存クラスに機能拡張させる記述をまとめています。
例えば以下のような拡張です。

private let formatter: DateFormatter = {
    let formatter: DateFormatter = DateFormatter()
    formatter.timeZone = NSTimeZone.system
    formatter.locale = Locale(identifier: "en_US_POSIX")
    formatter.calendar = Calendar(identifier: .gregorian)
    return formatter
}()

public extension Date {
    // Date->String
    func string(format: String = "yyyy-MM-dd'T'HH:mm:ssZ") -> String {
        formatter.dateFormat = format
        return formatter.string(from: self)
    }

    // String->Date
    init?(dateString: String, dateFormat: String = "yyyy-MM-dd'T'HH:mm:ssZ") {
        formatter.dateFormat = dateFormat
        guard let date = formatter.date(from: dateString) else { return nil }
        self = date
    }
}

Date(dateString: "2021-02-10T10:10:00Z")  // Date
date.string(format: "yyyy/MM/dd") // 2021/02/10

Constants.swiftでは、そのプロジェクトで使用する定数などを定義します。
従来の#define定義とか、他言語では、public static finalとか、constを使って定義していたものですね。

スポンサーリンク

Twitterでフォローしよう

おすすめの記事