web-dev-qa-db-ja.com

「シミュレータ」および「デバイス用」の「脂肪」Cocoa Touchフレームワークをエクスポートする方法

Xcode 6を使用すると、独自のDynamic Cocoa Frameworksを作成できます。

enter image description here

のため:

  • シミュレーターは引き続き32-bitライブラリーを使用します

  • app Storeに送信される2015年6月1日以降のアプリの更新には、64ビットのサポートが含まれ、iOS 8 SDKでビルドされる必要があります( developer.Apple.com

デバイスやシミュレータでプロジェクトを実行するには、ファットライブラリを作成する必要があります。つまり、フレームワークで32ビットと64ビットの両方をサポートします。

しかし、他のプロジェクトとの将来の統合のためにユニバーサル脂肪フレームワークを(exportする方法と、このライブラリを誰かと共有する方法)を見つけられませんでした。

再現する手順は次のとおりです。

  1. ONLY_ACTIVE_Arch=NOBuild Settingsを設定します

    enter image description here

  2. サポートarmv7 armv7s arm64 i386 x86_64Architecturesに追加します(確かに)

enter image description here

  1. フレームワークをビルドし、Finderで開きます。

enter image description hereenter image description here

  1. このフレームワークを別のプロジェクトに追加します

実結果:

しかし、結局のところ、このフレームワークを使用したプロジェクトをデバイスとシミュレーターで同時に実行することにはまだ問題があります。

  • フレームワークをDebug-iphoneosフォルダーから取得した場合-デバイスで機能し、シミュレーターでエラーが発生します:ld: symbol(s) not found for architecture i386

    xcrun lipo -info CoreActionSheetPicker
    

    Fatファイルのアーキテクチャ:CoreActionSheetPickerは次のとおりです。armv7 armv7s arm64

  • Debug-iphonesimulatorフォルダーからフレームワークを取得する場合-シミュレーターで動作します。デバイスにエラーがあります:ld: symbol(s) not found for architecture arm64

    xcrun lipo -info CoreActionSheetPicker
    

    ファットファイルのアーキテクチャ:CoreActionSheetPicker:i386 x86_64

では、デバイスやシミュレーターで動作する動的なフレームワークを作成する方法は?

この回答は Xcode 6 iOS Cocoa Touch Frameworkの作成-アーキテクチャの問題 に関連していますが、重複していません。


更新:

このケースで「ダーティハック」を見つけました。 以下の回答 を参照してください。誰かがもっと便利な方法を知っているなら-教えてください!

106
skywinder

この回答の実際は、2015年7月です。状況が変わる可能性が最も高いです。

TLDR;

現在、Xcodeにはユニバーサルファットフレームワークの自動エクスポート用のツールがないため、開発者はlipoツールを手動で使用する必要があります。また、 このレーダー によると、フレームワークのコンシューマーであるAppStore開発者に提出する前に、lipoを使用してフレームワークからシミュレータースライスを取り除く必要があります。

長い回答が続きます


トピックで同様の調査を行いました(回答の下部にあるリンク)。

配布に関する公式ドキュメントを見つけていないので、私の研究はApple開発者フォーラム、CarthageおよびRealmプロジェクトの調査、およびxcodebuildlipocodesignツールを使用した私自身の実験に基づいています。

これは、Apple Developer Forumsスレッドからの長い引用です(私から少しマークアップしています) 組み込みフレームワークを使用したアプリのエクスポート

フレームワークプロジェクトからフレームワークをエクスポートする適切な方法は何ですか?

現在、唯一の方法はあなたがやったことです。

  • シミュレーターとiOSデバイスの両方のターゲットをビルドします。
  • そのプロジェクトのXcodeのDerivedDataフォルダーに移動し、2つのバイナリを1つのフレームワークにリポします。ただし、Xcodeでフレームワークターゲットをビルドする場合は、ターゲット設定の「Build Active Architecture Only」を「NO」に調整してください。これにより、Xcodeは複数のバイナリタイプ(arm64、armv7など)のターゲットをビルドできます。これがXcodeからは動作するが、スタンドアロンバイナリとしては動作しない理由です。

  • また、スキームがリリースビルドに設定されていることを確認し、リリースに対してフレームワークターゲットをビルドする必要があります。ライブラリがロードされていないというエラーが引き続き発生する場合は、フレームワークのコードスライスを確認してください。

  • lipo -info MyFramworkBinaryを使用して、結果を調べます。

lipo -info MyFrameworkBinary

結果はi386 x86_64 armv7 arm64です

  • 現代のユニバーサルフレームワークには4つのスライスが含まれますが、さらに多くのスライスを含めることができます。i386 x86_64 armv7 arm64少なくともこの4つが表示されない場合は、アクティブアーキテクチャの構築設定が原因です。

これは、@ skywinderが回答で行ったのとほぼ同じプロセスを説明しています。

Carthageはlipoを使用 および Realmはlipoを使用 です。


重要な詳細

レーダーがあります: Xcode 6.1.1&6.2:シミュレータースライスを含むiOSフレームワークはApp Storeに送信できません およびそれに関する長い議論 Realm#116 および Carthage#188 これは特別な回避策で終わりました:

AppStore iOSフレームワークバイナリに送信する前に、シミュレータスライスから削除する必要があります

Carthageには、特別なコード CopyFrameworks および対応するドキュメントがあります:

このスクリプトは、ユニバーサルバイナリによってトリガーされる App Store提出バグ を回避します。

レルムには特別なスクリプトがあります: strip-frameworks.sh および対応するドキュメント:

この手順は、ユニバーサルバイナリをアーカイブするときに App Store提出バグ を回避するために必要です。

また、良い記事があります: Xcodeの動的ライブラリから不要なアーキテクチャを取り除く

私自身、Realmのstrip-frameworks.shを使用しましたが、修正なしで完璧に機能しましたが、もちろん誰でも最初から自由に作成できます。


この質問の別の側面が含まれているため、読むことをお勧めする私のトピックへのリンク:コード署名- iOS/OSXフレームワークの作成:他の開発者に配布する前にコード署名する必要がありますか?

78

これはそれほど明確な解決策ではありませんが、私が見つける唯一の方法があります:

  1. ONLY_ACTIVE_Arch=NOBuild Settingsを設定します

    • シミュレータ用のビルドライブラリ
    • デバイス用のビルドライブラリ
  2. フレームワークのコンソールProductsフォルダーで開く(フレームワークフォルダーを開いてそこからcd ..で開くことができます)

enter image description hereenter image description here

  1. Productsフォルダからthisスクリプトを実行します。このフォルダーにfat Frameworkを作成します。 (または3。4。で説明されているように手動で実行します)

または:

  1. このスクリプトでlipoを使用してこれら2つのフレームワークを結合します(YourFrameworkNameをフレームワーク名に置き換えます)

    lipo -create -output "YourFrameworkName" "Debug-iphonesimulator/YourFrameworkName.framework/YourFrameworkName" "Debug-iphoneos/YourFrameworkName.framework/YourFrameworkName"
    
  2. 既存のフレームワークの1つを新しいバイナリに置き換えます。

    cp -R Debug-iphoneos/YourFrameworkName.framework ./YourFrameworkName.framework
    mv YourFrameworkName ./YourFrameworkName.framework/YourFrameworkName
    

  1. 利益:./YourFrameworkName.framework-すぐに使用できるファットバイナリです!プロジェクトにインポートできます!

ワークスペースにないプロジェクトの場合:

here で説明されているように this Gist を使用することもできます。しかし、ワークスペース内のプロジェクトでは機能しないようです。

56
skywinder

@Stainlavの回答は非常に役に立ちましたが、代わりに2つのバージョンのフレームワーク(デバイス用とシミュレータ用)をコンパイルし、次のRun Script Phaseを追加して、実行中のアーキテクチャに必要なプリコンパイル済みフレームワークを自動的にコピーしました

echo "Copying frameworks for architecture: $CURRENT_Arch"
if [ "${CURRENT_Arch}" = "x86_64" ] || [ "${CURRENT_Arch}" = "i386" ]; then
  cp -af "${SRCROOT}/Frameworks/Simulator/." "${SRCROOT}/Frameworks/Active"
else
  cp -af "${SRCROOT}/Frameworks/Device/." "${SRCROOT}/Frameworks/Active"
fi

このように、App Storeに提出する際にlipoを使用して脂肪のフレームワークを作成せず、レルムのstrip-frameworks.shを使用して不要なスライスを削除する必要はありません。

10
odm

基本的にこれのために私は非常に良い解決策を見つけました。これらの簡単な手順に従うだけです。

  1. ココアタッチフレームワークを作成します。
  2. ビットコードを有効に設定します。
  3. ターゲットを選択し、スキームの編集を選択します。実行を選択し、情報タブからリリースを選択します。
  4. 他の設定は必要ありません。
  5. シミュレーターがx86アーキテクチャーで実行されるときに、シミュレーターのフレームワークを構築します。
  6. Project Navigatorの[製品]グループをクリックして、.frameworkファイルを見つけます。
  7. それを右クリックし、Finderで表示をクリックします。任意のフォルダにコピーして貼り付けます。個人的には「シミュレータ」という名前を好みます。
  8. 次に、汎用iOSデバイスのフレームワークを構築し、手順6〜9に従います。フォルダの名前を「シミュレータ」ではなく「デバイス」に変更します。
  9. デバイスの.frameworkファイルをコピーして、他のディレクトリに貼り付けます。私は両方の直下のスーパーディレクトリを好みます。したがって、ディレクトリ構造は次のようになります。
    • デスクトップ
    • 端末
      • MyFramework.framework
    • シミュレーター
      • MyFramework.framework
    • MyFramework.frameworkターミナルを開いて、デスクトップに移動します。次のコマンドの入力を開始します。

lipo -create 'device/MyFramework.framework/MyFramework' 'simulator/MyFramework.framework/MyFramework' -output 'MyFramework.framework/MyFramework'

以上です。ここでは、MyFramework.framework内に存在するMyFrameworkバイナリのシミュレーターとデバイスバージョンをマージします。シミュレーターやデバイスを含むすべてのアーキテクチャー向けに構築されるユニバーサルフレームワークを取得します。

2
Nrip

更新したいだけです この素晴らしい答え by @odm。 Xcode 10以降、CURRENT_Arch変数はビルドアーキテクチャを反映しなくなりました。そこで、代わりにプラットフォームをチェックするようにスクリプトを変更しました。

echo "Copying frameworks for platform: $PLATFORM_NAME"
rm -R "${SRCROOT}/Frameworks/Active"
if [ "${PLATFORM_NAME}" = "iphonesimulator" ]; then
    cp -af "${SRCROOT}/Frameworks/Simulator/." "${SRCROOT}/Frameworks/Active"
else
    cp -af "${SRCROOT}/Frameworks/Device/." "${SRCROOT}/Frameworks/Active"
fi

また、コピーする前にターゲットディレクトリをクリアするための行を追加しました。サブディレクトリ内の追加のファイルが上書きされないことに気付いたからです。

2
Dorian Roy

私の答えは以下の点をカバーしています:

  • シミュレーターとデバイスの両方で機能するフレームワークを作成する

  • 「脂肪」ココアタッチフレームワークをエクスポートする方法(シミュレーターとデバイスの両方)

  • アーキテクチャx86_64の未定義のシンボル

  • ld:アーキテクチャx86_64のシンボルが見つかりません

ステップ1:最初にシミュレータターゲットを使用してフレームワークを構築します

ステップ2:シミュレーターのビルドプロセスが成功した後、デバイスターゲット選択または汎用iOSデバイス選択を使用してフレームワーク用にビルドします

ステップ3:フレームワークターゲットを選択し、そのために[ビルドフェーズ]で[実行スクリプトの追加]を選択し、以下のスクリプトコードをコピーします)

ステップ4:最後に、再度ビルドします。フレームワークは、シミュレーターとデバイスの両方の互換性を備えています。万歳!!!!

[注:最終ステップ4の前に、互換性のある両方のフレームワークを用意する必要があります(シミュレーターとデバイスアーキテクチャーが互換性がある場合は、上記のステップ1と2を正しく実行してください)

参考画像をご覧ください。

enter image description here

enter image description here

以下のコードをシェル領域に配置します。

#!/bin/sh


UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal


# make sure the output directory exists

mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"


# Step 1. Build Device and Simulator versions

xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_Arch=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_Arch=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build


# Step 2. Copy the framework structure (from iphoneos build) to the universal folder

cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"


# Step 3. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directory

SIMULATOR_Swift_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/."

if [ -d "${SIMULATOR_Swift_MODULES_DIR}" ]; then

cp -R "${SIMULATOR_Swift_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"

fi


# Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory

lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"


# Step 5. Convenience step to copy the framework to the project's directory

cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"


# Step 6. Convenience step to open the project's directory in Finder

open "${BUILD_DIR}/${CONFIGURATION}-universal"
1