こんにちは。ソフトウェアエンジニアの@kitasukeです。
今回は、インクルードされたPods.xcconfigとインクルードしたxcconfigでそれぞれ定義されたフレームワークパスを足し合わせて、Build Settingsに値を反映させる方法を紹介します。
この方法は、下記のUmbrella Frameworkの作成で必要でした。 具体的な使用例を知りたい場合はこちらも参照してください。
XcodeのBuild Settingsを直接いじるのは、メンテナンスの観点からあまり積極的にやりたくないです。 主な理由として、値が変更されていたら太文字になるという分かりにくさと、多くの設定項目からその太文字の値を探すのは一苦労です。
CocoaPods を使う場合は、フレームワークパスなどの設定は自動生成される Pods.xcconfig が面倒を見てくれます。 ただし継承関係にないxcconfig間で値のオーバーライドをできないので、CocoaPodsとは別にマニュアルでフレームワークパスなどの設定が必要な場合にその全てをxcconfig上でセットするにはコツが要ります。
Build Settingsの値がxcconfigによって反映される順番は、Xcodeの Project に紐づくxcconfig -> Target に紐づくxcconfig -> Built Settings にセットされた値です。 したがって、xcconfigが上記の継承関係にある場合は、下記のように値のオーバーライドが可能です。
// project.xcconfig
VALUE = project
// target.xcconfig
#include "project.xcconfig"
VALUE = $(inherited) target
// Build Settings
Value = project target
しかし、xcconfigが継承関係になくただインクルードしてるだけの場合は、下記のようにインクルードされた値は無視されてしまいます。
// Pods.xcconfig
FRAMEWORK_SEARCH_PATHS = "${PODS_CONFIGURATION_BUILD_DIR}/xxx"
// app.debug.xcconfig
#include "../Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${BUILT_PRODUCTS_DIR}/zzz"
// Build Settings
FRAMEWORK_SEARCH_PATHS = build/Debug-iphoneos/zzz
継承関係にない場合は同じキーに値をセットすると片方は無視されるので、 Variable Substitution を利用してそれぞれ違うキーを足し合わせます。
https://pewpewthespells.com/blog/xcconfig_guide.html#VariableSubstitution
つまり、Pods.xcconfigでは PODS_FRAMEWORK_SEARCH_PATHS というキーに値をセットして、app.debug.xcconfigでそれを FRAMEWORK_SEARCH_PATHS にセットします。
// app.debug.xcconfig
#include "../Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"
FRAMEWORK_SEARCH_PATHS = ${PODS_FRAMEWORK_SEARCH_PATHS} "${PODS_CONFIGURATION_BUILD_DIR}/xxx"
// Build Settings
FRAMEWORK_SEARCH_PATHS = build/Debug-iphoneos/xxx build/Debug-iphoneos/zzz
Pods.xcconfigが生成する FRAMEWORK_SEARCH_PATHS をそのまま利用する場合もあるので、通常用の FRAMEWORK_SEARCH_PATHS と自身のxcconfigで足し合わせる用の PODS_FRAMEWORK_SEARCH_PATHS の両方を下記のように生成します。
# Podfile
post_install do |installer|
work_dir = Dir.pwd
installer.aggregate_targets.each do |target|
target.user_build_configurations.each do |key, name|
xcconfig_filename = "#{work_dir}/Pods/Target Support Files/#{target}/#{target}.#{name}.xcconfig"
xcconfig = File.read(xcconfig_filename)
framework_search_paths = xcconfig.match(/(?<=FRAMEWORK_SEARCH_PATHS = )(.*)/)
header_search_paths = xcconfig.match(/(?<=HEADER_SEARCH_PATHS = )(.*)/)
other_ldflags = xcconfig.match(/(?<=OTHER_LDFLAGS = )(.*)/)
File.open(xcconfig_filename, "a") do |file|
file.puts("PODS_FRAMEWORK_SEARCH_PATHS = #{framework_search_paths}")
file.puts("PODS_HEADER_SEARCH_PATHS = #{header_search_paths}")
file.puts("PODS_OTHER_LDFLAGS = #{other_ldflags}")
end
end
end
end
上記のPodfileから pod install
すると下記のように生成されます。
下記のように同じ値を持ったキーが複数できますが、xcconfigで管理できる利点を考えるとそれほど問題ではないでしょう。
FRAMEWORK_SEARCH_PATHS = $(inherited) xxx
HEADER_SEARCH_PATHS = $(inherited) xxx
OTHER_LDFLAGS = $(inherited) xxx
PODS_FRAMEWORK_SEARCH_PATHS = $(inherited) xxx
PODS_HEADER_SEARCH_PATHS = $(inherited) xxx
PODS_OTHER_LDFLAGS = $(inherited) xxx
今回は、 generate_multiple_pod_projects と incremental_installation を有効にしているので、ターゲットの取得に aggregate_targets を使います。
install! 'cocoapods', :generate_multiple_pod_projects => true, incremental_installation: true
そうでない場合は、下記のように pods_project を使うのが良いと思います。
post_install do |installer|
installer.pods_project.targets.each do |target|
...
end
end
CocoaPodsの利便性に頼りながらxcconfigでBuild Settingsの値を管理するのが良いと思います。 ターゲット共通の設定なら継承関係にできますが、コンフィグレーションによって内容が変わる場合は上記のような対応が必要だと思います。 このように、できる限りxcconfigを利用して楽に運用しましょう。