Embedded Framework で Carthage を使ったさいの問題と回避策

  • 23
    Like
  • 2
    Comment
More than 1 year has passed since last update.

(2015-11-29:訂正と追記をおこないました)

AppExtension を含むアプリケーションを開発していると ContainerアプリとExtension でコードを共有したいことがあります。
そんなときは Embedded Framework のビルドターゲットにコードを置けば アプリと Extension で共有することができます。

しかし、Embedded Framework のライブラリ管理に Carthage を使ったところ 実機にてアプリを起動した直後に次のエラーが発生しました:

dyld: Library not loaded: @rpath/Alamofire.framework/Alamofire
Referenced from: 
../Hoge.app/Frameworks/HogeKit.framework/HogeKit
Reason: no suitable image found. Did find:
..Hoge.app/Frameworks/HogeKit.framework/Frameworks/Alamofire.framework/Alamofire: mmap() error 1 at address=0x1016F8000, size=0x00054000 segment=__TEXT in Segment::map() mapping /Hoge.app/Frameworks/HogeKit.framework/Frameworks/Alamofire.framework/Alamofire

というわけで、Embedded Framework で Carthage を使ったときのエラーについての調査結果と回避策をまとめます。

開発環境

  • Xcode 7.1.1
  • Swift
  • Carthage 0.10.0

結論

結論としては Embedded Framework ターゲットのライブラリ管理に Carthage は利用できないと考えました。
というのも、次の issue を読むかぎり アプリと Emdedded Framework をひとつのプロジェクトとして開発するのには利用できないと思ったからです:

I'm not sure that copy-frameworks will work with embedded frameworks.Carthage is designed to use top-level frameworks so that versions of those dependencies can be resolved in ways that meet the requirements for all dependencies.
https://github.com/Carthage/Carthage/issues/771

とはいえ、この issue を見つけたときには回避策を見つけていたので 手順を書いておきます。

回避策

回避策としては プロジェクトの設定について 次のふたつを修正しました:

  • ENABLE_BITCODE を NO にする
  • Embedded Framework の Build Phase でCarthage/Build 以下のframeworksをコピーする

ENABLE_BITCODE を NO にする

Emdedded Framework が Bitcodeありでビルドされ・Carthage/Build 以下の framework がBitcodeなしでビルドされていました。

otool コマンドで確認した結果は次のとおりです:

$ otool -l Frameworks/HogeKit.framework/HogeKit | grep __LLVM
  segname __LLVM
   segname __LLVM
$ otool -l Frameworks/HogeKit.framework/Frameworks/SwiftyJSON.framework/SwiftyJSON | grep __LLVM

ENABLE_BITCODE を NO にすると Archive した ipa(AdHoc Deployment)はエラーなく実機で起動できました。
ただ、ENABLE_BITCODEを修正しても Run(Debug) すると 同じエラーが発生し問題が残ります。

追記 (2015-11-29)

ENABLE_BITCODE を YES にしたまま Archive し Save for AppStore Deployment した ipa を再度確認したところ、ともに Bitcode を含んだ executable を生成していました:

$ otool -l -arch arm64 Hoge.app/Hoge | grep __LLVM
  segname __LLVM
   segname __LLVM
$ otool -l -arch arm64 Hoge.app/Frameworks/HogeKit.framework/HogeKit | grep __LLVM
  segname __LLVM
   segname __LLVM
$ otool -l -arch arm64 Hoge.app/Frameworks/HogeKit.framework/Frameworks/SwiftyJSON.framework/SwiftyJSON | grep __LLVM
  segname __LLVM
   segname __LLVM

ただ、ENABLE_BITCODE を YES にしたまま Build したときは App の executable のみ Bitcode を生成していません:

$ otool -l -arch arm64 Hoge | grep __LLVM
$ otool -l -arch arm64 Frameworks/HogeKit.framework/HogeKit | grep __LLVM
  segname __LLVM
   segname __LLVM
$ otool -l -arch arm64 Frameworks/HogeKit.framework/Frameworks/SwiftyJSON.framework/SwiftyJSON | grep __LLVM
  segname __LLVM
   segname __LLVM

このあと実機で Run させると 起動直後に dyld: Library not loaded が発生します。
App と それ以外のframeworks で Bitcode の有無が異なることとの因果関係について 今後わかったことがあればまた追記します。

Build Phase で frameworksをコピーする

Embedded Framework ターゲットの Build Phase を "Run Script" にcopy-frameworks.shをおこなう設定を追加していましたが、これを外して “Copy Files” に設定を追加しました。

Copy File の設定手順としては README に記載してあるとおりです:

To do this, create a new “Copy Files” build phase with the “Frameworks” destination, then add the framework reference there as well.
https://github.com/Carthage/Carthage#adding-frameworks-to-unit-tests-or-a-framework

よくわかっていないこと

今回は ENABLE_BITCODE を NO にしましたが、Carthage では Bitcodeに対応できているようです:

2015/10/28 追記: BITCODE_GENERATION_MODE の自動設定に対応した Carthage 0.9.4 がリリースされました.
https://blog.ymyzk.com/2015/10/carthage-and-bitcode/

ですので、ENABLE_BITCODE を修正する必要はないはずです。ですが 調べてみると:

$ otool -l Carthage/Build/iOS/SwiftyJSON.framework/SwiftyJSON | grep __LLVM

carthage update しても --no-use-binaries しても framework には Bitcodeが生成されていないようです。

疑問は残ったままですが、今後わかったことがあれば追記します。

訂正 (2015-11-29)

carthage update で作られる frameworks は すべて Bitcode を含んでいました。@ikesyo さん, ご指摘ありがとうございます :)

no-use-binaries オプションなしで carthage update した framework のバイナリが universal binary であることをまず確認します:

$ otool -f -v Build/iOS/SwiftyJSON.framework/SwiftyJSON
Fat headers
fat_magic FAT_MAGIC
nfat_arch 4
architecture i386
    cputype CPU_TYPE_I386
..
architecture x86_64
    cputype CPU_TYPE_X86_64
..
architecture armv7
    cputype CPU_TYPE_ARM
..
architecture arm64
    cputype CPU_TYPE_ARM64
..

続いて シミュレーター用(x86_64) のバイナリに Bitcode が含まれていないこと・iPhone 5s+用(arm64) のバイナリに Bitcode が含まれていることを確認:

$ otool -l Build/iOS/SwiftyJSON.framework/SwiftyJSON -arch x86_64 | grep  __LLVM
$ otool -l Build/iOS/SwiftyJSON.framework/SwiftyJSON -arch arm64 | grep  __LLVM
  segname __LLVM
   segname __LLVM

勉強になりました :)

参考資料

(余談) Emdedded Framework を CocoaPods で使う

Emdedded Framework のライブラリ管理に CocoaPods を使うには Private podspec を作る必要がありそうです:

you should rather create a private podspec for "SampleAppKit" and declare its dependencies in there.
https://github.com/CocoaPods/CocoaPods/issues/3440#issuecomment-119180268

参考資料