CarthageでRxSwiftのRxBlockingフレームワークとRxTestフレームワークとプロジェクトにインポートしようとしてハマりました。
この2つのフレームワークはテスト用のフレームワークなのにメインのアプリターゲットに取り込んでしまったのが原因。
忘れないように解決方法を記事に残そうと思います。
実行環境
- Xcode9.2
- carthage 0.26.2
【やったこと】CarthageでRxSwiftのフレームワークをいれた
標準的なCarthageでのインポート方法を実行していました。
まずCartfile
を作り、RxSwiftを指定します。
github "ReactiveX/RxSwift" ~> 4.0
updateでcarthageでRxSwiftフレームワークをクローン&ビルドします。
carthage update --platform iOS
...
*** Checking out RxSwift at "4.1.0"
*** xcodebuild output can be found in /var/folders/h7/mls39jf56szdt9yhcmhtv5b80000gn/T/carthage-xcodebuild.yf00eV.log
*** Building scheme "RxCocoa-iOS" in Rx.xcworkspace
*** Building scheme "RxSwift-iOS" in Rx.xcworkspace
*** Building scheme "RxBlocking-iOS" in Rx.xcworkspace
*** Building scheme "RxTests-iOS" in Rx.xcworkspace
RxCocoa、RxSwift、RxBlocking、RxTestsの4つのフレームワークがビルドされました。
これをアプリプロジェクトに指定していきます。
プロジェクトエディターのGeneral > Linked Frameworks and Librariesで4つのフレームワークをインポートしました。
また、Build PhaseにRun Scriptを追加し、下記コマンドも追加、input fileに4つのフレームワークのパスの追加もしました。
/usr/local/bin/carthage copy-frameworks
$(SRCROOT)/Carthage/Build/iOS/RxCocoa.framework
$(SRCROOT)/Carthage/Build/iOS/RxSwift.framework
$(SRCROOT)/Carthage/Build/iOS/RxBlocking.framework
$(SRCROOT)/Carthage/Build/iOS/RxTest.framework
【問題発生】XCTestがないという実行時エラーでアプリが落ちる
ところがこの状態でアプリをビルドすると実行時エラーでアプリが落ちてしまいます。
コンソールから出力されたエラーはこちら。
dyld: Library not loaded: @rpath/XCTest.framework/XCTest
Referenced from: /Users/xxxxxx/Library/Developer/CoreSimulator/Devices/7A8D7F6F-7F02-4524-9AB6-511490751CC1/data/Containers/Bundle/Application/090C5FE7-7B5F-4609-B11C-552C46C145F2/MVVMExample.app/Frameworks/libswiftXCTest.dylib
Reason: image not found
エラー内容は「libswiftXCTest.dylibがないため、XCTest.frameworkがロードできませんでした」というもの。
XCTestフレームワークってどこから来たのだろうと首をかしげて調査に時間がかかってしまいました。
【解決方法】RxBlockingとRxTestはテストターゲットに指定しよう
考えて見ればRxBlocking、RxTestsはテスト用のフレームワークでXCTestフレームワークに依存しているとのことでした。
しかしメインアプリのターゲットではXCTestフレームワークはインポートされていないのでXCTestフレームワークが見つからずアプリが落ちるようです。
なのでRxBlocking、RxTestsはテストターゲットにインポートが必要です。
アプリターゲットで指定していたRxBlocking、RxTestsを消してテストターゲットに移し替えるました。
まず、メインアプリターゲットのGeneral > Linked Frameworks and LibrariesでRxBlockingとRxTestのフレームワークを消しました。
RxCocoaとRxSwiftのみを残した状態にします。
また同じくメインアプリターゲットのBuild PhasesのRun ScriptでRxBlockingとRxTestのinput fileのパスを消しました。
そして、テストターゲットにRxBlocking、RxTestsをインポートします。
ところで、テストターゲットのプロジェクトエディターのGeneralにはLinked Frameworks and Librariesの項目がありません。
なのでBuild Phases > Link Binary With Librariesで指定をしてあげます。
アプリターゲットと同じく、Run Scriptを追加してフレームワークのコピー指定も忘れずにします。
こうすることでビルドが上手くいくはずです!
【追記】2018/01/01
🐶 mono (@_mono)さんさんから指摘があり、RxBlockingはXCTestに依存していないそう。
RxBlockingのみ入れた場合は試しました?RxTestはダメですがRxBlockingはメインアプリターゲットに含めても問題にならないという認識です🧐
— 🐶 mono (@_mono) 2018年1月1日
(メインアプリにRxBlockingを入れて使うことの是非は別として)
試したところRxBlockingをアプリターゲットに含めてビルドができました。
ただしRxBlockingは公式ではテスト用のみを目的にしたフレームワークだそうです。
RxBlockingは使うこと自体は完全に非推奨になってますね( ´・‿・`)以前、メインアプリで便宜的に同期的に結果が欲しいときに一瞬使おうとしましたがやめました🐶https://t.co/WLTWXDKzy0
— 🐶 mono (@_mono) 2018年1月1日
> Don't use these operators in production apps. These operators are only meant for testing purposes.
Don't use these operators in production apps. These operators are only meant for testing purposes.
これらのオペレーターを本番用アプリで使用しないでください。これらのオペレーターはテストの目的で作られました。(筆者訳)
RxBlockingは「アプリターゲットに入れられるけど非推奨」とのことです。
まとめるとアプリターゲットとテストターゲットに入れるフレームワークは以下となります。
ターゲット | フレームワーク | 補足 |
---|---|---|
アプリターゲット | RxCocoa | UIKitのクラスのエクステンションを提供するのであると便利。 |
アプリターゲット | RxSwift | RxSwiftの大元フレームワーク |
テストターゲット | RxBlocking | アプリターゲットに入れられるけど非推奨。テストに使いましょう。 |
テストターゲット | RxTests | XCTest依存でアプリターゲットに入れられない。テストに使いましょう。 |
参考
↓は同じくRxBlockingをメインターゲットにしてXCTest.frameworkがないために実行時エラーになった方のイシューです。
dyld: Library not loaded: @rpath/XCTest.framework/XCTest · Issue #1717 · Carthage/Carthage