CocoaPodsで、アプリ側のターゲットに複数のPodsターゲットをリンクするときの設定


CocoaPodsを使っているとき、アプリ側のターゲットとPodsターゲットが1:1の場合は、特に何も設定せず使用できるのですが、
アプリ側のターゲットとPodsターゲットが1:多の場合は、アプリ側のbuild_configurationsにどれか１つのPodsターゲットの設定しか書き込まれないため、
Podsのターゲットをいくつか分割している場合面倒なことになります。

## 例

例えば、下記のような構成にしている場合、

* global context(targetで囲まれていない箇所)は、アプリ(Dev, AdHoc, Production, Unittest)共通のライブラリをインストール
* 試験導入的な意味で"Experimental"というtargetを作って、そこにお試しのライブラリをインストール
* サーバAPIとの通信が必要なアプリで、まだAPIが実装されていないから"Temporary"というtargetを作って、Mock/Stubライブラリを一時的にインストール
* Unittestだけで使用するライブラリをインストール

１つのアプリターゲットに大して、Podsターゲットが複数存在した場合、ライブラリはすべてインストールされますが、
build_settingsやfontファイルなどのリソースファイルなどは、どれか１つのPodsターゲットのものしか設定しないため
"Experimental"にfontファイルがふくまれていて、それをコピーしなければならないのにも関わらずコピーされないという状況に陥ったりします。

これを解決するには、`post_install`を利用して、ライブラリインストールが完了した後に、自前でXcodeの設定を書き換えます。


```ruby: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"
```