iOSプロジェクトにライブラリを導入する時、
多くの人は Cocoapods
or Carthage
を用いると思います。
しかし先日、やむを得ず
どちらのやり方もできないライブラリを導入する機会がありました。
この導入に際して、たくさんのエラーとぶつかったので、記録として残します。
※以下そのライブラリを、仮名 R.framework
と称すことにします。
前提
frameworkとは
そもそもframeworkとは、いくつかのリソースを1つまとめたパッケージで、
Projectファイルに埋め込むことで、framework内のリソースを
Projectで使えるようになります。
※参考:
参考文献で分かりやすいと思った一文:
Frameworkとは、動的共有ライブラリや nibファイル、イメージファイル、ローカライズファイル、ヘッダファイル、ドキュメント等のリソースファイルを1つのパッケージにまとめたディレクトリ
今回のケース
R.framework
は自作ではなく、知り合いの方が作成したもの。
自分の扱うProjectに一度cocoapodsで導入して、ビルド成功させます。
そこで生成できた R.framework
を用います。
cocoapodsで導入すると不都合がことがあったというレアケースだと思います。
開発環境
OS: Mac
Xcode: v10.1
こうやればできるはず
まず、ぼくが試した正常パターンであろう手順を説明します。
1. 自分のProjectにpod install&ビルド成功
本当に導入したいProjectでも、自身のサンプルProjectでもよいですが、
一度pod installで R.framework
を導入します。
$ bundle exec pod install
※自分の場合、bundlerでcocoapodsのバージョン管理してるので上記コマンド。
pod install完了後、Xcodeで MyProject.scworkspace
を立ち上げ、
ビルド成功させます。
2. R.frameworkを取得
ビルドを成功させたら、Xcodeの設定ファイルを開いて、
下記の矢印ボタンを押します。Finderが開きます。
DerivedDataというフォルダが開かれます。
このDerivedDataとは、いわゆるXcodeのキャッシュです。
普段は、Xcodeを完全にクリーンするときに削除するものですね。
このDerivedData上に R.framework
が生成されます。
階層は下記。
DerivedData/MyProject-{ランダム文字}/Build/Products/Debug-iphonesimulator/R/R.framework
これで R.framework
の取得完了です。
3. R.frameworkをProjectに埋め込む
R.framework
をProjectファイルにドラッグ&ドロップします。
※今回は FunctionConfirm
Projectに導入してみます。
するとこんな感じのポップアップが出てきます。
Projectフォルダに R.framework
ファイルを保存した方がよいので、
Copy items If needed
にはチェックを入れておきましょう。
※チェックを入れ忘れた場合は、あとで Build Phases>Copy File
で設定すれば同様のことができたりします。(詳細省略)
R.framework
をドラッグ&ドロップした箇所に入れるだけなら、
Create folder references
のままに、Groupに入れたい場合は、
Create groups
にチェックします。
Targetはよしなに必要なものを選んでおいて、Finishボタンを押します。
4. Embedded BinariesにR.frameworkを追加
つまり、 FunctionConfirm
プロジェクトに埋め込むファイル一覧に、
R.framework
を追加するということです。
General>Embedded Binaries
に R.framework
を+します。
5. 重複するR.frameworkを削除
上記4を行うと、下の Linked Frameworks and Librares
に、
R.framework
が2つあることが分かります。
3の操作と4の操作で、計2回追加されてしまったんですね。
複数ある必要は特にないので、片方を削除しておきます。
6. ビルドで完了
ビルドして成功すれば完了です。
コード内で import R
ができるようになります。
本題:困ったこと
ここからが本題です。出くわしたエラーたちを紹介していきます。
そもそもpod install&ビルドできない
エラーログ:
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
エラー見ても分からない、なんやねん...
原因と対処
そもそもcocoapodsのバージョンがダメでした。初歩的...
実は $ bundle exec pod install
ではなく、
$ pod install
をやっちゃっていたんですね...
bundlerでcocoapodsを管理していないと、
勝手にcocoapodsのバージョンが最新になってしまいます。
R.framework
が最新のcocoapodsに対応していませんでした。
cocoapodsのバージョンをbundlerで管理するように変えました。
Pods参照できない
エラーログ:
/Users/XXX/XXX/XXX/XXX/Pods/Pods/Target Support Files/Pods-XXX/Pods-XXX.debug.xcconfig: unable to open file
なんやねん...
Pods/Pods ってなんやねん...
原因と対処
cocoapodsをダウングレードすると起きる現象らしいです。
project.pbxprojファイル内に、
name = Pods;
path = Pods;
という記述を見つけました。これか...
片方消したらビルド通るようになりました。
※参考:
R.framework見つからないとか言われる
R.framework
を埋め込んでビルドしようとしたときのエラーです。
エラーログ:
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
※エラーログちょっと違うかも...とりあえずnot foundがあったのは確実。
ちゃんと Embedded Binaries
したのになぜ...
原因と対処
Target複数あったのを忘れていました
自分あほ...
Targetが複数ある場合は、どのTargetの Embedded Binaries
にも
R.framework
を追加する必要があります。
自分が設定したTargetの Embedded Binaries
に設定してなかった
だけだったので、修正したらエラーは直りました。
Simulatorではビルドできるのに実機だとできない
今回ぼくが一番悩んだエラーです。
warningログ:
ld: warning: ignoring file /Users/.../R.framework/R, file was built for x86_64 which is not the architecture being linked (arm64): /Users/.../R.framework/R
エラーログ:
PhaseScriptExecution failed with a nonzero exit code
R.framework
ファイルなんか無視されるし、
権限上のなにかしらでダメらしい...
ぼくが悪かったなら謝るから、何が悪かったか教えてくれよ...
※参考:
状態を確認する
教えてくれないなら、こちらから調べにいくしかありません。
調べていたら、 arm64
やら x86_64
というのは、
iOS・tvOS・watchOSデバイスのアーキテクチャであることが分かりました。
この記事の表がとても参考になりました。
Simulator(x86_64)はビルドできるけど、実機(arm64)はビルドできない...
もしかして、R.framework
がx86_64はサポートしているけど、
arm64はサポートしてない的な...という仮説を考えました。
実際に調べてみます。
$ xcrun lipo -info [binary]
を使えば、
サポートしているデバイスアーキテクチャが調べられるとのこと。
※参考:https://teratail.com/questions/61071
別の有名なライブラリ Perfect.framework
と比べてみました。
$ xcrun lipo -info Perfect.framework/Perfect
Architectures in the fat file: Perfect.framework/Perfect are: i386 x86_64 armv7 arm64
やはりPerfect。i386、x86_64、armv7、arm64をサポートしていました。
続いて R.framework
を調べてみます。
$ xcrun lipo -info R.framework/R
Non-fat file: R.framework/R is architecture: arm64
...!?
原因
原因は、「R.framework
が実機をサポートしていなかったから」でした。
なぜこんなことに。。。ふと、気付きました。
このDerivedData上に
R.framework
が生成されます。
階層は下記。
DerivedData/MyProject-{ランダム文字}/Build/Products/Debug-iphonesimulator/R/R.framework
... Debug-iphonesimulator/ だと...!?
そうです、この R.framework
はSimulator階層にあったもの。
だからSimulator専用の R.framework
だったのです。
無事、ぼくが悪いことが判明しました。ごめんなさい。
対処
謝るのは簡単ですが、それでは何も解決しません。
対応策として考えられるのは、
「Simulatorも実機もサポートしている R.framework
を作成すること」
です。
MyProject.scworkspace
のビルドを実機を対象に行うと、
DerivedData/MyProject-{ランダム文字}/Build/Products/Debug-iphoneos/R/
階層に R.framework
が見つかります。
しかし、これは逆に「実機しかサポートしていない R.framework
」です。
これでは実機でビルドできるだけで、根本の解決にはなりません。
この記事によると、
Simulatorも実機もサポートしているframeworkを、
Universal Framework
と呼ぶことが分かりました。これを作ればいい...!!
...
_人人人人人人人人人人人人人_
> 作り方がわからない <- NOW <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄
frameworkを自作していた場合の Universal Framework
の作り方は調べて分かりましたが、今回は知り合いの方が作った場合なので、
自分ではどうすれば良いか分かりませんでした_(:3」∠)_
※やり方分かる方いましたら教えてくださいm(__)m
今回は、知り合いの方に Universal Framework
の作成を依頼しました。
※Framework作成系参考:
- http://www.abt.jp/kb/2013/06/mac-os-x-create-framework/
- https://www.raywenderlich.com/2430-how-to-create-a-framework-for-ios
- https://medium.com/@syshen/create-an-ios-universal-framework-148eb130a46c
- https://qiita.com/wai21/items/3a2a7a7170ab9c5c1952
- https://qiita.com/wheel/items/156248c98faa433aecfe
まとめ
以上、独自のライブラリ(.framework)を組み込んで苦労した話でした。
今回自分がエラーを対処するために気をつけたポイントは下記。
- エラーログを見る
- 状態を確認する
- ググる
そしてライブラリの扱いの件について思ったまとめは下記。
- cocoapods等で取得したライブラリを無闇に.frameworkにしない
- というか、ライブラリはcocoapods等で取得したまま利用する
- 知見として、自分でライブラリ作る経験は必要ですわ...
以上です。
※その他参考: