はじめに
ライブラリ編の続きです。
おすすめ構成を実践してみました。今回アーキテクチャに関しては特に触れないですがプレゼンテーションロジックをどっかに置きたいと思い PresentationModel
というよくわからないものを置いています。
つくったアプリ
つくったのは3画面程度の小さなアプリでランダムにどっかの都道府県の今日の天気を教えてくれるアプリです。
よくわからないアバター表示と履歴に保存する機能があります。
スタート | お天気 | 履歴(あり) | 履歴(なし) |
---|---|---|---|
下記 API を利用
- livedoor天気のWeb API(商用利用不可)
- joe schmoe(アバター表示用)
フォルダ構成
フォルダ構成はこんな感じ。Source と Resourece で分けて最初からある AppDelegate
とかは Sourece 直下に置き、他は Screens (画面)と PresentationModels (プレゼンテーション層?っぽいやつ)に分けています。
モデル部分は別モジュールに分割しています。今回は Models でまとめていますが適切な粒度でさらに分割してもいいと思います。(ビルド時間の短縮は重要!!)
モジュール作成方法は下記のように File -> New -> Target -> Framework でできます。
画面構成
画面構成は Storyboard を使うのか xib を使うのか全部コードでやるのかとプロジェクトごとに色々あると思いますが私のおすすめは1画面1 Storyboard です。以前記事書いたのでよかったらどうぞ(storyboard とのつきあい方)
Storyboard だと Segue
や Container View
が使えるので xib ではなく Storyboard を使っています。Segue
がいいのかは好みが分かれると思いますが Unwind Segue
とかたまに使いたくなるので私はわりと Segue
を使っています。画面間は Storyboard Reference
でつないでいます。
こんな感じ
ついでにアプリの rootViewController
に関しても少し。アプリ起動時の画面を条件によって変えたい場合がたまにあると思いますが、そういう場合 AppDelegate
で window.rootViewController
を差し替えたりしてたのですがこの方法あまり良くないなと思い今回のやつは Main.storyboard と ViewController.swift を残して下記のように ViewController
の childViewController
で初期画面を設定しています。これで常にエントリポイントは ViewController
になるんでいいんじゃないかなと思いますがこれでやったことないので要検討です。(もしかしたらなんか弊害あるかも??)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
final class ViewController: UIViewController { private let resolver = AppResolverImpl() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. // 起動時に画面を差し替える場合はここで設定する showStartViewController() } private func showStartViewController() { let rootViewController = R.storyboard.start.instantiateInitialViewController()! let start = rootViewController.viewControllers.first as! StartViewController start.presentationModel = resolver.resolveStartPresentationModel() view.addSubview(rootViewController.view) addChild(rootViewController) rootViewController.didMove(toParent: self) } } |
使用ライブラリ
ライブラリ | 用途 |
---|---|
mac-cain13/R.swift | リソース管理用 |
realm/SwiftLint | Lint ツール |
mono0926/LicensePlist | ライセンス表記用(ライセンス表記は大事😇) |
ishkawa/DIKit | DI 用 |
realm/realm-cocoa | DB |
ishkawa/APIKit | 通信用 |
SwiftyBeaver/SwiftyBeaver | ログ出力用 |
Quick/Quick | テスト用 |
Quick/Nimble | テスト用 |
ライブラリ編 で記載したように Carthage -> SPM -> Pods の順で導入しました。R.swift, SwiftLint, LicensePlist を Pods で導入し残りは Carthage で導入しました。今回 SPM の出番はなかったです。。。SVG 表示用に mchoe/SwiftSVG を SPM で入れようかと思ったのですが今回の SVG 表示には対応してないみたいであきらめました。。。(WKWebView
で表示してます)
cocoapods のバージョンを開発者間で揃えるため bundler を使ってます。(パッケージマネージャ編参考)
APIKit
を閉じ込めたいと思って色々やってたら通信周りはほぼ自作になってしまいました。。。(HTTP ステータスの判定部分はもうちょい工夫がいる気がする)
Xcodeのバージョンをそろえる
開発者間で Xcode のバージョンが違うとたまに思わぬ弊害があるので Run Script を追加してバージョンが違えばエラーを出すようにしました。
方法
- プロジェクトのトップディレクトリに .xcode-version ファイルを追加
- Run Script を追加
.xcode-version
1 2 |
REQUIRED_XCODE_VERSION='11.5' REQUIRED_XCODE_PRODUCT_BUILD_VERSION='11E608c' |
Run Script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
. $SCRIPT_INPUT_FILE_0 # バージョン表記を4桁左詰めゼロパディング形式に変換 REQUIRED_XCODE_VERSION_ACTUAL=`echo $REQUIRED_XCODE_VERSION | tr -d . | xargs printf '%-4d' | tr ' ' 0` if [ $REQUIRED_XCODE_VERSION_ACTUAL != $XCODE_VERSION_ACTUAL ]; then echo "error: Detected wrong Xcode version. Expected=$REQUIRED_XCODE_VERSION_ACTUAL, actual=$XCODE_VERSION_ACTUAL." exit 1 fi if [ $REQUIRED_XCODE_PRODUCT_BUILD_VERSION != $XCODE_PRODUCT_BUILD_VERSION ]; then echo "error: Detected wrong Xcode build version. You might be using beta version. Expected=$REQUIRED_XCODE_PRODUCT_BUILD_VERSION, actual=$XCODE_PRODUCT_BUILD_VERSION." exit 2 fi |
これで Xcode 11.5 以外でビルドするとエラーになります。
ライブラリとか色々スクリプト追加すると今回のやつはこんな感じになりました。
スキーム
アプリ開発をする際は本番用サーバと開発用サーバなどサーバのつなぎ先を変えたりといったことがよくあります。そういう時のためにそれぞれスキームを追加します。やり方は以前書いたこの記事参考(Xcodeで本番アプリと開発アプリをわけるベストプラクティス)
- Project -> Info -> Configurations で Deug を複製する
- 複製したConfigurationの名称を変更する(Debug copy -> Develop)
- Build Settings -> Swift Compiler - Custom Flags にフラグを追加する(開発なら DEVELOP ダミーなら DUMMY など)
- Build Settings -> Product Bundle Identifier で Bundele id を変更する
- Build Settings -> Packaging -> Product Name でアプリ名を変更する
- Build Settings -> Asset Catalog Compiler - Options -> Asset Catalog App Icon Set Name でアプリアイコンを変更する
- Manage Schemes... -> Duplicate でスキームを複製する
- スキームの名称を変更する(Copy of PiyoApp -> PiyoAppDev)
- スキームの Build Configration を変更する(Run 以外の Test なども適宜変更する)
コードで下記のように分岐させる。別モジュールだとこのフラグは有効ではないので注意が必要です。追加後に Pods でエラーが出る場合はもう一度 pod install
する。おそらく Pods にも Configuration が自動で追加されるがその関連でエラーが出る。
1 2 3 4 5 6 7 8 9 10 11 12 |
var serverURL: String { #if DEVELOP // 開発用URL return "https://example.com/piyoDev" #elseif DUMMY // ダミー用URL(ダミーはローカルのJSONファイル読み込む想定なので空文字でもOK) return "https://example.com/piyoDummy" #else // 本番用URL return "https://example.com/piyo" #endif } |
今回のアプリは開発用サーバとかないので Dummy だけ追加してプロジェクト内のファイルを読みにいくようにしました。
おわりに
実際のプロジェクトだと fastlane
とか jenkins
とか設定すると思いますがその辺はいつも他のメンバがやってくれてたので私はわかんないです。。。(いつか覚えたい)
SwiftUI とかあるし今後はどんどん構成が変わっていくかもしれないです。今のとこ(2020/06/28)のあくまで個人的なおすすめ構成です。
あんま Coverage よくなかった。。。
コメント