CocoaPodsを使っているとき、アプリ側のターゲットとPodsターゲットが1:1の場合は、特に何も設定せず使用できるのですが、
アプリ側のターゲットとPodsターゲットが1:多の場合は、アプリ側のbuild_configurationsにどれか1つのPodsターゲットの設定しか書き込まれないため、
Podsのターゲットをいくつか分割している場合面倒なことになります。
例
例えば、下記のような構成にしている場合、
- global context(targetで囲まれていない箇所)は、アプリ(Dev, AdHoc, Production, Unittest)共通のライブラリをインストール
- 試験導入的な意味で"Experimental"というtargetを作って、そこにお試しのライブラリをインストール
- サーバAPIとの通信が必要なアプリで、まだAPIが実装されていないから"Temporary"というtargetを作って、Mock/Stubライブラリを一時的にインストール
- Unittestだけで使用するライブラリをインストール
1つのアプリターゲットに大して、Podsターゲットが複数存在した場合、ライブラリはすべてインストールされますが、
build_settingsやfontファイルなどのリソースファイルなどは、どれか1つのPodsターゲットのものしか設定しないため
"Experimental"にfontファイルがふくまれていて、それをコピーしなければならないのにも関わらずコピーされないという状況に陥ったりします。
これを解決するには、post_install
を利用して、ライブラリインストールが完了した後に、自前でXcodeの設定を書き換えます。
Podfile
require './lib/helpers/pod_app_helper' # アプリターゲットのxcconfigやリソースファイルコピースクリプトを設定するための処理
platform :ios, '7.0'
APP_TARGGETS = %w[MyApp MyAppAdHoc MyAppProd]
TESTING_TARGETS = %w[MyAppTests MyAppUITests]
ALL_TARGETS = APP_TARGGETS + TESTING_TARGETS
pod_lib_installer = Proc.new do |name, pod_args|
args = [name.to_s, pod_args].compact
pod(*args)
end
link_with ALL_TARGETS
# 共通でインストールするライブラリ
pod 'Facebook-iOS-SDK', '3.16.1'
pod 'AFNetworking', '2.3.1'
pod 'SDWebImage', '3.7.1'
pod 'Underscore.m', '~> 0.2.1'
pod 'UICKeyChainStore', '~> 1.0.5'
# AdHocのみでインストールするライブラリ
target "MyAppAdHoc", :exclusive => true do
pod 'TestFlightSDK', '~> 3.0.2'
end
# お試しで実験的にインストールするライブラリ
target "Experimental", :exclusive => true do
link_with ALL_TARGETS
pod 'FontAwesomeKit', '~> 2.1.7' # ライブラリにFontファイルがふくまれているのでリソースのコピーが必要
end
# サーバ側未実装の部分があって、APIのstub/mockとか一時的に使用するもの
target "Temporary", :exclusive => true do
link_with APP_TARGETS - %w[MyAppProd]
pod 'OHHTTPStubs', '~> 3.1.4'
end
test_libs = {
'TKRGuard' => '~> 1.0.2',
'OCMock' => '~> 3.0.2',
'OHHTTPStubs' => '~> 3.1.4'
}
# Unittestで使用するライブラリ
target "MyAppTests", :exclusive => true do
test_libs.each(&pod_lib_installer)
end
# UITestで使用するライブラリ
target "MyAppUITests", :exclusive => true do
test_libs.each(&pod_lib_installer)
pod 'KIF', '~> 3.0'
end
post_install do |installer|
# Copy Pods Resourcesのスクリプト追加やxcconfig追加の処理を追加
self.extend(Pod::AppHelper)
# アプリ側のxcode projectを開く
Xcodeproj::Project.open(*Dir.glob('*.xcodeproj')).tap do |project|
project.targets.each do |target|
# CocoaPodsがどれかひとつPod targetのresourcesをコピーしているdefaultの`Copy Pods resources`は役にたたないので削除
target.shell_script_build_phases.each do |phase|
phase.remove_from_project if phase.name == 'Copy Pods Resources'
end
# アプリターゲット毎にxcconfigを設定し、使用するPodsターゲットからリソースコピーを実行するスクリプトを作成する
if target.name == "MyApp"
pod_target_names = ['', 'Experimental', 'Temporary']
add_extra_copy_pods_resources(target, pod_target_names)
config_file_path = 'etc/App-dev.xcconfig'
replace_project_base_configuration(project, target, config_file_path)
elsif target.name == "MyAppAdHoc"
pod_target_names = ['', 'Experimental', 'Temporary', 'MyAppAdHoc']
add_extra_copy_pods_resources(target, pod_target_names)
config_file_path = 'etc/App-AdHoc.xcconfig'
replace_project_base_configuration(project, target, config_file_path)
elsif target.name == "MyAppProd"
pod_target_names = ['', 'Experimental']
add_extra_copy_pods_resources(target, pod_target_names)
config_file_path = 'etc/App-Prod.xcconfig'
replace_project_base_configuration(project, target, config_file_path)
elsif target.name == "MyAppTests"
pod_target_names = ['', 'Experimental', 'MyAppTests']
add_extra_copy_pods_resources(target, pod_target_names)
config_file_path = 'etc/LogicTests.xcconfig'
replace_project_base_configuration(project, target, config_file_path)
elsif target.name == "MyAppUITests"
pod_target_names = ['', 'Experimental', 'MyAppUITests']
add_extra_copy_pods_resources(target, pod_target_names)
config_file_path = 'etc/UITests.xcconfig'
replace_project_base_configuration(project, target, config_file_path)
end
# preprocessor macroの設定を追加する場合もここで
if TESTING_TARGETS.include?(target.name)
target.build_configurations.each do |config|
# Define preprocessor
if config.name == "Debug"
preprocessor_definitions = config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] || ['$(inherited)']
preprocessor_definitions = [preprocessor_definitions] unless preprocessor_definitions.instance_of?(Array)
# #define TESTING 1
preprocessor_definitions.push('TESTING=1').uniq!
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = preprocessor_definitions
end
end
end
end
project.save # プロジェクトを保存
end
end
pod_app_helper.rb
module Pod
module AppHelper
# PodsディレクトリにあるPodsターゲット毎のリソースコピースクリプトをアプリターゲットに登録
def add_extra_copy_pods_resources(target, pod_target_names)
# Appends Extra `Copy Pods resources` to target
extra_copy_pods_resources = pod_target_names.map {|pod_target_name|
script_name = pod_target_name.empty? ? "Pods-resources.sh" :"Pods-#{pod_target_name}-resources.sh"
{
:script_name => script_name,
:script_build_phase_title => "Copy Pods Resources(#{script_name})",
:shell_script => "\"${SRCROOT}/Pods/#{script_name}\""
}
}.reject {|phase|
target.shell_script_build_phases.map {|phase| phase.name }.include?(phase[:script_build_phase_title])
}
extra_copy_pods_resources.each do |phase|
build_phase = target.new_shell_script_build_phase(phase[:script_build_phase_title])
build_phase.shell_script = phase[:shell_script]
end
end
# Podsターゲット毎に用意されている`Pods-xxxx.xcconfig`をまとめてインクルードしたxcconfigをプロジェクトに登録する
def replace_project_base_configuration(project, target, config_file_path)
config_file = project.files.find {|file| file.path == config_file_path } || project.new_file(config_file_path)
target.build_configurations.each do |config|
config.base_configuration_reference = config_file
end
end
module_function :add_extra_copy_pods_resources, :replace_project_base_configuration
end
end
App-dev.xcconfig
#include "Pods/Pods.xcconfig"
#include "Pods/Pods-Experimental.xcconfig"
#include "Pods/Pods-Temporary.xcconfig"