CocoaPodsでFirebaseをmain target以外に入れることができない問題(解決済)


iGhostというアプリで @_mono さんと話したことをシェア。

やりたいこと

3層アーキテクチャ(Presentation/Domain/Infra)でいうInfra層をembedded frameworkとして分割し、Infra層内にFirestoreを隠蔽したい。
しかし、そこに立ちはだかる問題。

Firebaseをmain target以外に入れることができない。

どういうことかというと、

(パターン1) Infra.frameworkにのみFirebaseを入れる

Podfileを書きます。

platform :ios, '11.2'
use_frameworks!

target 'Infra' do
  pod 'Firebase/Core'
  pod 'Firebase/Firestore'
end

こうしたところ、ソースコードにて import Infra しようとしても error: missing required module 'Firebase' と出てビルドできません。

(パターン2)abstract targetでmainとInfra層どちらにもFirebaseを含める

Embedded Framework使いこなし術 - Qiita のコメントを参考に、次のようなPodfileを記述。
(アプリのmain target名がAwesomeAppです)

platform :ios, '11.2'
use_frameworks!

abstract_target '_' do
  pod 'Firebase/Core'
  pod 'Firebase/Firestore'

  target 'AwesomeApp' do
  end

  target 'Infra' do
  end
end

すると、実行時にはコンソールログに次のようなエラーが吐き出されます。

objc[28375]: Class FIRAIdentifiers is implemented in both /Users/XXXX/Library/Developer/Xcode/DerivedData/AwesomeApp-bdyzjyetywwdoscsrzlhepywlttz/Build/Products/Debug-iphonesimulator/Infra.framework/Infra (0x106fba478) and /Users/XXXX/Library/Developer/CoreSimulator/Devices/C8EB6FE1-BD9E-4D95-A39A-9EF9CD4DC572/data/Containers/Bundle/Application/A3B7F562-51BE-4D71-88FD-C585CC6BF8F5/AwesomeApp.app/AwesomeApp (0x105a93348). One of the two will be used. Which one is undefined.

この状態だと、画面遷移時に以下のリンクのようなループが発生しクラッシュしてしまいます。

ios - Infinite loop viewDidAppear on launch - Stack Overflow
https://stackoverflow.com/questions/42086327/infinite-loop-viewdidappear-on-launch/42086899

(無限ループ自体は info.plist の FirebaseAutomaticScreenReportingEnabled を NO にすれば解決しますが、それは本質的ではありませんね)

とりあえずの解決策

真の解決策が判明しました
眉に唾をつけながら読んでください。

とりあえずの解決策としては、パターン1・2ともに、
main targetのbuild settingsで Other Linker Flags に空文字を入れる(inheritさせない) と動くようになります。

ただ、空文字で上書きすると pod install 時に以下のような警告が出るのが気になるところですが…。

[!] The `AwesomeApp [Debug]` target overrides the `OTHER_LDFLAGS` build setting defined in `Pods/Target Support Files/Pods-AwesomeApp/Pods-AwesomeApp.debug.xcconfig'. This can lead to problems with the CocoaPods installation
    - Use the `$(inherited)` flag, or
    - Remove the build settings from the target.

[!] The `AwesomeApp [Release]` target overrides the `OTHER_LDFLAGS` build setting defined in `Pods/Target Support Files/Pods-AwesomeApp/Pods-AwesomeApp.release.xcconfig'. This can lead to problems with the CocoaPods installation
    - Use the `$(inherited)` flag, or
    - Remove the build settings from the target.

他のライブラリをPodで入れる場合には嵌まりそうなので、リスクを理解した上で自己責任でお試し下さい。
あるいはもっと適切な対処法がありましたら是非コメントお寄せ下さい。

諦めてこういう正攻法やるほうがいいのかなあ…。

真の解決策(2018/3/1追記)

機会あって Firebase の人に直接聞いて解決しました。

platform :ios, '11.2'
use_frameworks!

target 'Infra' do
  pod 'Firebase/Core'
  pod 'Firebase/Firestore'

  target 'AwesomeApp' do
    inherit! :search_paths
  end
end

ポイント1: inherit! :search_paths

これまで意味も分からず使っていましたが、意味は以下の通りです。

inherit!

Sets the inheritance mode for the current target.

Parameters

...
+ :search_paths The target inherits the search paths of the parent only.

そのまんまですが。
…字面はそのまんまなんですが、本質的には……どういうことなんでしょうね?

ポイント2: Main targetはInfraのサブターゲットとする

なんで? 逆じゃないの? と思ったんですが、よく考えるとこれが正しい。
何故なら、テストターゲットの Podfile の書き方は以下のようになりますよね。(参考: https://guides.cocoapods.org/syntax/podfile.html

target 'Hoge' do

  target 'HogeTests' do
    inherit! :search_paths
    # Pods for testing

  end
end

Target Dependencies が HogeHogeTests という方向になるとき、 HogeTestsHoge のサブターゲットです。
であれば、 Target Dependencies が InfraAwesomeApp という方向であるなら、同じように AwesomeAppInfra のサブターゲットとなる。
当然の帰結でした。

結論

というわけで、よくよく考えるとそうなるよなというところで解決策が落ち着きました。
この記述であれば、先述したような諸問題は起こらず、正しくアプリを機能させることができます。