Skip to content

Instantly share code, notes, and snippets.

@kitasuke
Created June 8, 2020 00:47
Show Gist options
  • Save kitasuke/acb4715fa740cd89b1a2733179ad5cab to your computer and use it in GitHub Desktop.
Save kitasuke/acb4715fa740cd89b1a2733179ad5cab to your computer and use it in GitHub Desktop.
xcconfig-variable-substituion.md

継承関係にない複数のxcconfigで値をオーバーライドする方法

こんにちは。ソフトウェアエンジニアの@kitasukeです。

今回は、インクルードされたPods.xcconfigとインクルードしたxcconfigでそれぞれ定義されたフレームワークパスを足し合わせて、Build Settingsに値を反映させる方法を紹介します。

この方法は、下記のUmbrella Frameworkの作成で必要でした。 具体的な使用例を知りたい場合はこちらも参照してください。

https://medium.com/@kitasuke/%E3%83%9E%E3%83%AB%E3%83%81%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%AAios%E3%82%A2%E3%83%97%E3%83%AA%E3%81%AE%E8%B5%B7%E5%8B%95%E9%80%9F%E5%BA%A6%E6%94%B9%E5%96%84-3c18f6ad47ff?source=friends_link&sk=27e9cc3485d571af171ce9667abedebc

概要

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_projectsincremental_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を利用して楽に運用しましょう。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment