はじめに
数年経つプロジェクトに途中からXcodeGenを入れるのは骨折れる作業が、、、
そんな中、気になる内容を見つけた
お、やってみよ!
導入
0. 前提
#735は、まだマージされていないので未完成です。
PRの説明でもそれは記載されています。
いくつか対応がなされていないようです。
なので、使用してみてメリデメの調査を含めてやっていきます。
1. インストール
PRのブランチを指定してXcodeGenを落としてきます。
その後、インストールしましょう。
$ git clone -b spec-generation https://github.com/yonaskolb/XcodeGen.git
$ cd XcodeGen
$ make install
※ make install で少し時間がかかるので注意
※ ブランチ指定するために普通にcloneしていますが、他の方法でも指定できるならそれでも問題ありません
2. project.ymlの生成
プロジェクトのルートディレクトリまで移動し、コマンドを実行します。
$ xcodegen migrate --project xxx.xcodeproj --spec project.yml
(xxx.xcodeproj
は各自のプロジェクト名に変更してください)
すると、ルートディレクトリにproject.yml
が生成されているはずです。
と言うわけで簡単に行えました!
問題点と解決策
実際に運用するとなるといくつかの問題点が見えてきました。
(まだPRが未完成なので、当たり前っちゃ当たり前ですが、、笑)
1. 設定したConfigurationが生成されない
以下のように、3つを設定していましたが
実際にymlに生成されたものは以下でした。
これは他のプロジェクトでも試しましたが、debug/releaseしか生成されないようです。
なので自前で書く必要があります。が、configsの部分だけで問題ありません。
なぜなら、設定やアプリへの紐付け部分は吐き出されています。
(長いので間を省略しています)
このようにdebug/develop/release
の3つ分の設定が、吐き出されているのがわかります。
なので、configsで定義し直しあげることで、ちゃんと動くようになります。
設定してあげないと、Xcodegen generate
コマンドでこんなエラーが出ます。
設定に関しては、この記事を参考にすると良いでしょう。
Xcodeプロジェクトの生成ツール「XcodeGen」のセットアップ&操作方法
2. 設定したschemeが生成されない
以下のように、3つを設定していましたが
実際にymlには何も生成されていませんでした。
こちらもConfiguration同様に追加する必要があります。
最低限、動くようになる例を置いておきます。
schemes:
MailerTestAppDebug:
build:
targets:
MailerTestApp: all
MailerTestAppTests: [test]
MailerTestAppUITests: [test]
MailerTestAppDevelop:
build:
targets:
MailerTestApp: all
MailerTestAppRelease:
build:
targets:
MailerTestApp: all
アプリ名は各自のでお願いします。
実際はもっと細かく設定してください。
3. xcconfigの紐付けが生成されない
こちらも自前で追加しましょう
configFiles:
Debug: configs/Debug.xcconfig
Develop: configs/Develop.xcconfig
Release: configs/Release.xcconfig
そもそもymlに全て移行してしまうのも手ではありますが、、
4. 生成されたymlが肥大化
余計なコードが入ってしまい肥大化がみられました。
では、何が余計なのでしょうか、、?
以下、いくつかの例を紹介します。
① Clang(コンパイラ)の設定
Xcodeにデフォルトで設定されている、コンパイラの部分が吐き出されることで肥大化します。
SchemeやTestのTargetごとに吐かれるため、以下のようなコード群がいくつも生成されます。
CLANG_ANALYZER_NONNULL: YES
CLANG_CXX_LANGUAGE_STANDARD: gnu++0x
CLANG_CXX_LIBRARY: libc++
CLANG_ENABLE_MODULES: YES
CLANG_ENABLE_OBJC_ARC: YES
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING: YES
CLANG_WARN_BOOL_CONVERSION: YES
CLANG_WARN_COMMA: YES
CLANG_WARN_CONSTANT_CONVERSION: YES
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS: YES
CLANG_WARN_DIRECT_OBJC_ISA_USAGE: YES_ERROR
CLANG_WARN_EMPTY_BODY: YES
CLANG_WARN_ENUM_CONVERSION: YES
CLANG_WARN_INFINITE_RECURSION: YES
CLANG_WARN_INT_CONVERSION: YES
CLANG_WARN_NON_LITERAL_NULL_CONVERSION: YES
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF: YES
CLANG_WARN_OBJC_LITERAL_CONVERSION: YES
CLANG_WARN_OBJC_ROOT_CLASS: YES_ERROR
CLANG_WARN_RANGE_LOOP_ANALYSIS: YES
CLANG_WARN_STRICT_PROTOTYPES: YES
CLANG_WARN_SUSPICIOUS_MOVE: YES
CLANG_WARN_UNREACHABLE_CODE: YES
CLANG_WARN__DUPLICATE_METHOD_MATCH: YES
特定の意図がない限りはいじることがないので、ymlからごっそり消して良いかと思います。
(ymlに記載しなくても、デフォルトで設定されるので)
② GCC(コンパイラ)の設定
①と同じですが、以下のようなコード群がいくつも生成されます。
GCC_WARN_64_TO_32_BIT_CONVERSION: YES
GCC_WARN_ABOUT_RETURN_TYPE: YES_ERROR
GCC_WARN_UNDECLARED_SELECTOR: YES
GCC_WARN_UNINITIALIZED_AUTOS: YES_AGGRESSIVE
GCC_WARN_UNUSED_FUNCTION: YES
GCC_WARN_UNUSED_VARIABLE: YES
こちらもymlからごっそり消して良いかと思います。
③ Carthege(framework)の設定が入ってしまう
このように列挙されてしまいます。
- inputFiles:
- $(SRCROOT)/Carthage/Build/iOS/Alamofire.framework
- $(SRCROOT)/Carthage/Build/iOS/Realm.framework
- $(SRCROOT)/Carthage/Build/iOS/RealmSwift.framework
- $(SRCROOT)/Carthage/Build/iOS/SwiftyStoreKit.framework
- $(SRCROOT)/Carthage/Build/iOS/Reachability.framework
- $(SRCROOT)/Carthage/Build/iOS/KeychainAccess.framework
- $(SRCROOT)/Carthage/Build/iOS/RxSwift.framework
- $(SRCROOT)/Carthage/Build/iOS/RxCocoa.framework
- $(SRCROOT)/Carthage/Build/iOS/Nuke.framework
- $(SRCROOT)/Carthage/Build/iOS/NukeWebP.framework
dependenciesにライブラリ(framework)を記載しておけば、XcodeGenが勝手に紐づけてくれるのでごっそり消してしまいましょう。
④ Cocoapod(framework)の設定が入ってしまう
- inputFiles:
- ${PODS_ROOT}/Target Support Files/Pods-delish/Pods-delish-frameworks.sh
- ${BUILT_PRODUCTS_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework
- ${BUILT_PRODUCTS_DIR}/FBSDKLoginKit/FBSDKLoginKit.framework
- ${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework
name: '[CP] Embed Pods Frameworks'
outputFiles:
- ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaLumberjack.framework
- ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKCoreKit.framework
- ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKLoginKit.framework
- ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework
runOnlyWhenInstalling: false
script: '"${PODS_ROOT}/Target Support Files/Pods-delish/Pods-delish-frameworks.sh"'
shell: /bin/sh
showEnvVars: false
OTHER_LDFLAGS:
- $(inherited)
- -ObjC
- -l"c++"
- -l"sqlite3"
- -l"stdc++"
- -l"z"
- -framework
- '"AVFoundation"'
- -framework
- '"Accounts"'
- -framework
- '"AddressBook"'
・
・
・
pod install
で勝手に追加されるので、ごっそり消してしまいましょう。
⑤ 1ファイルごとに登録されてしまう
以下のように列挙された形で出力されます。
sources:
- app/AppDelegate.swift
- app/Extensions/UIImageView+Extensions.swift
- app/Extensions/UIView+Extensions.swift
- app/Extensions/UITableView+Extensions.swift
- app/Extensions/UICollectionView+Extensions.swift
・
・
・
このように一覧で出るため、グループ化はされていないのです。
なので、以下のようにしてまとめてあげると良いでしょう。
sources:
- path: app
- path: Extensions
type: directory
XcodeGen
のtype directory
で簡潔になりました。
4. すべてのファイルがルートディレクトリにきてしまう(Xcode上で)
これは「③ 1ファイルごとに登録されてしまう」に付随しますが、1ファイルごとに登録されてしまうために起こります。
こちらは、sourcesで読み込みたいファイルを指定することで解決できます。
(なので、同様に不要なpathの参照をまとめるなり・消すなりする必要があります)
考察
メリット
「かゆいオプションを描き出さなくて済む」
これに尽きるかと思います。
buildのsettingsもそうですが、たとえば、
- Run script
- Notification/today などのextension
など、見比べてymlに書いていくのは以外に面倒だったりします。笑
デメリット
問題点に基本的に集約されますが、
PRがまだ未完成
- 一部の設定が反映されない
- ymlの肥大化
「一部」の設定と書いてますが、まあまああるのでなんとも言えないラインではあります、、
現状
既存プロジェクトにおいては、こちらを使ってみるのはアリだと思います。
というのも、基本的には吐き出されないScheme/Configurationの設定だけ、自分で追記すればビルドできるので、ymlが肥大化しようが関係ないです。
(sourcesでディレクトリ構成も設定する必要あり)
既存でこちらを導入する場合は、こちら(PRのXocdeGen)でつくったymlを叩き台にして、ymlファイルを整理していくのが良いかと思います。
ただ、新規アプリでXcodeGenを導入するなら、自前でymlを書いた方良いかと思います。
行数も少なくてすみ、なにより読みやすいかと思います。
(作ったばかりのプロジェクトも含む)
#終わりに
XcodeGenのメリットは、よく「コンフリクトしなくなること」が例に挙げられますが、そこはそんなにメリットではなく
(普通に開発ルールを守っていればそんなに大きくおきないし、解消するのも難しくない)
こちらの記事にもありますが
(株式会社キュリオシティソフトウェア - 2019年上半期のiOSアプリ開発振り返り)
わざわざSchemeで設定を変えるみたいな変なことやっててもConfigurationに差し替え可能
Scheme間やConfiguration間の設定値の差分がコードなのでわかりやすい
つまり、XcodeのGUIでしか閲覧・編集できなかったことが、一覧で見れることの方が大きいです。
既存でこちらを導入する場合は、その一覧で見れるメリットが少し薄れてしまいます。
(行数多いし、無駄なコードが設定が増えてしまうため比較しづらい)
まだまだ、開発途中なので本体にmergeされることを期待しつつ、、と言う感じでしょうか笑