iGhostというアプリで @_mono さんと話したことをシェア。
Firebaseに強いSwiftラヴ🐶くん https://t.co/Rg8JX3gNwC #iGhost #チャットアプリ pic.twitter.com/Qcen4RZcGh
— takasek (@takasek) 2018年2月19日
やりたいこと
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で入れる場合には嵌まりそうなので、リスクを理解した上で自己責任でお試し下さい。
あるいはもっと適切な対処法がありましたら是非コメントお寄せ下さい。
Firebaseをmain targetにしか入れられない問題、しょうがないのでprotocolをinfra layerで定義してapplication layer(main target)で実装して注入するって方法しかなかった気がする
— try! JK教師💓 koutalou (@koutalou) 2018年2月19日
諦めてこういう正攻法やるほうがいいのかなあ…。
真の解決策(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 が Hoge
← HogeTests
という方向になるとき、 HogeTests
は Hoge
のサブターゲットです。
であれば、 Target Dependencies が Infra
← AwesomeApp
という方向であるなら、同じように AwesomeApp
は Infra
のサブターゲットとなる。
当然の帰結でした。
結論
というわけで、よくよく考えるとそうなるよなというところで解決策が落ち着きました。
この記述であれば、先述したような諸問題は起こらず、正しくアプリを機能させることができます。