Как экспортировать "толстый" Cocoa Touch Framework (для симулятора и устройства)?

С Xcode 6 мы получаем возможность создавать собственные динамические Cocoa Frameworks,

Потому что:

  • Симулятор еще использую 32-bit библиотека

  • начиная с 1 июня 2015 г. обновления приложений, представленные в App Store, должны включать поддержку 64-разрядных систем и должны быть встроены в iOS 8 SDK ( developer.apple.com)

Мы должны сделать толстую библиотеку для запуска проекта на устройствах и симуляторах. т.е. поддерживает как 32, так и 64 бит в Frameworks.

Но я не нашел никаких руководств, как экспортировать универсальный толстый Framework для будущей интеграции с другими проектами (и поделиться этой библиотекой с кем-то).

Вот мои шаги для воспроизведения:

  1. Задавать ONLY_ACTIVE_ARCH=NO в Build Settings

  2. Добавить поддержку armv7 armv7s arm64 i386 x86_64 в Architectures (наверняка)

  1. Создайте Framework и откройте его в Finder:

  1. Добавить эту структуру в другой проект

Фактический результат:

Но, в конце концов, у меня все еще есть проблема с запуском проекта с этой платформой на устройствах и симуляторе одновременно

  • если я возьму рамки из Debug-iphoneos папка - работает на устройствах и выдает ошибку на симуляторах: ld: symbol(s) not found for architecture i386

    xcrun lipo -info CoreActionSheetPicker
    

    Архитектуры в толстом файле: 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 - проблемы с архитектурой, но они не повторяются.


Обновить:

Я нашел "грязный взлом" для этого случая. Смотрите мой ответ ниже. Если кто-то знает более удобный способ - пожалуйста, дайте мне знать!

6 ответов

Решение

Актуальность этого ответа: июль 2015 года. Скорее всего, все изменится.

TLDR;

В настоящее время XCode не имеет инструментов для автоматического экспорта универсальной толстой платформы, поэтому разработчик должен прибегнуть к ручному использованию lipo инструмент. Также в соответствии с этим радаром перед отправкой в ​​AppStore разработчик, который является потребителем фреймворка, также должен использовать lipo соскоблить кусочки симулятора с каркаса.

Более длинный ответ следует


Я провел аналогичное исследование в теме (ссылка внизу ответа).

Я не нашел никакой официальной документации о распространении, поэтому мое исследование было основано на изучении форумов Apple Developer, проектов Carthage и Realm и моих собственных экспериментах с xcodebuild, lipo, codesign инструменты.

Вот длинная цитата (с небольшой разметкой от меня) из ветки Apple Developer Forums Экспорт приложения со встроенной платформой:

Как правильно экспортировать фреймворк из фреймворкового проекта?

На данный момент единственным способом является именно то, что вы сделали:

  • Постройте цель как для симулятора, так и для устройства iOS.
  • Перейдите в папку DerivedData Xcode для этого проекта и объедините два двоичных файла в единую структуру. Однако, когда вы создаете цель платформы в XCode, удостоверьтесь, что изменили настройку цели 'Build Active Architecture Only' на 'NO'. Это позволит Xcode построить цель для нескольких типов бинарников (arm64, armv7 и т. Д.). Вот почему он работает с XCode, но не как отдельный двоичный файл.

  • Также вы захотите убедиться, что схема настроена на сборку Release и построить целевую платформу против релиза. Если вы все еще получаете библиотеку с ошибкой загрузки, проверьте фрагменты кода в платформе.

  • использование lipo -info MyFramworkBinary и изучить результат.

lipo -info MyFrameworkBinary

Результат i386 x86_64 armv7 arm64

  • Современные универсальные фреймворки будут включать 4 слайса, но могут включать больше: i386 x86_64 armv7 arm64 Если вы не видите хотя бы 4, это может быть связано с настройкой Build Active Architecture.

Это описывает процесс почти так же, как это сделал @skywinder в своем ответе.

Вот как Карфаген использует липо, а Царство использует липо.


ВАЖНАЯ ИНФОРМАЦИЯ

Существует радар: Xcode 6.1.1 & 6.2: Платформы iOS, содержащие фрагменты симулятора, не могут быть отправлены в App Store, и долгое обсуждение вокруг этого по Realm#1163 и Carthage#188, которое закончилось специальным обходным решением:

перед отправкой в ​​AppStore iOS Framework двоичные файлы должны быть удалены с фрагментов симулятора

Карфаген имеет специальный код: CopyFrameworks и соответствующий документ:

Этот скрипт работает вокруг ошибки отправки в App Store, вызванной универсальными двоичными файлами.

В Realm есть специальный скрипт: strip-frameworks.sh и соответствующий фрагмент документации:

Этот шаг необходим для обхода ошибки отправки в App Store при архивировании универсальных двоичных файлов.

Также есть хорошая статья: Удаление нежелательных архитектур из динамических библиотек в XCode.

Я сам использовал Realm's strip-frameworks.sh который работал для меня отлично без каких-либо модификаций, хотя, конечно, любой может написать один с нуля.


Ссылка на мою тему, которую я рекомендую прочитать, потому что она содержит еще один аспект этого вопроса: подписывание кода - Создание фреймворков iOS/OSX: необходимо ли кодировать их перед распространением среди других разработчиков?

Это не очень понятное решение, но есть только один способ, который я нахожу:

  1. Задавать ONLY_ACTIVE_ARCH=NO в Build Settings

    • Сборка библиотеки для симулятора
    • Сборка библиотеки для устройства
  2. Открыть в консоли Products папку для вашего фреймворка (вы можете открыть его, открыв папку фреймворка и cd .. оттуда)

  1. Запустите этот скрипт из Products папка. Это создает толстый Framework в этой папке. (или сделайте это вручную, как описано ниже в 3. 4.)

Или же:

  1. Объедините эти 2 фреймворка, используя липосакцию этим скриптом (замените YourFrameworkName на имя вашего фреймворка)

    lipo -create -output "YourFrameworkName" "Debug-iphonesimulator/YourFrameworkName.framework/YourFrameworkName" "Debug-iphoneos/YourFrameworkName.framework/YourFrameworkName"
    
  2. Замените новым двоичным файлом одну из существующих платформ:

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

  1. Прибыль: ./YourFrameworkName.framework - готовый к использованию жирный бинарный файл! Вы можете импортировать его в свой проект!

Для проекта, которого нет в Workspaces:

Вы также можете попробовать использовать эту суть, как описано здесь. Но похоже, что это не работает для проектов в рабочих пространствах.

Ответ @Stainlav был очень полезен, но вместо этого я скомпилировал две версии фреймворка (одну для устройства и одну для симулятора), а затем добавил следующее 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

Таким образом, я не пользуюсь lipo чтобы создать толстую структуру ни царства strip-frameworks.sh удалить ненужные фрагменты при отправке в App Store.

Я просто хочу обновить этот замечательный ответ @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

Я также добавил строку, чтобы очистить целевой каталог перед копированием, поскольку заметил, что в противном случае дополнительные файлы в подкаталогах не будут перезаписаны.

В основном для этого я нашел очень хорошее решение. вам просто нужно следовать этим простым шагам.

  1. Создайте каркас касания какао.
  2. Установите битовый код включенным на номер
  3. Выберите вашу цель и выберите редактировать схемы. Выберите Run и выберите Release из вкладки Info.
  4. Никаких других настроек не требуется.
  5. Теперь создайте структуру для любого симулятора, поскольку симулятор работает на архитектуре x86.
  6. Нажмите на группу продуктов в Project Navigator и найдите файл.framework.
  7. Щелкните правой кнопкой мыши по нему и выберите Показать в поиске. Скопируйте и вставьте его в любую папку, лично я предпочитаю название "симулятор".
  8. Теперь создайте инфраструктуру для универсального устройства iOS и выполните шаги с 6 по 9. Просто переименуйте папку в "устройство" вместо "симулятор".
  9. Скопируйте файл устройства.framework и вставьте его в любой другой каталог. Я предпочитаю немедленный супер каталог обоих. Таким образом, структура каталогов теперь становится:
    • рабочий стол
    • устройство
      • MyFramework.framework
    • имитатор
      • MyFramework.framework
    • MyFramework.framework Теперь откройте терминал и перейдите на рабочий стол. Теперь начните вводить следующую команду:

lipo-создать 'устройство /MyFramework.framework/MyFramework' 'симулятор /MyFramework.framework/MyFramework' -output 'MyFramework.framework/MyFramework'

и это все. Здесь мы объединяем симулятор и версию устройства двоичного файла MyFramework, представленного внутри MyFramework.framework. Мы получаем универсальный фреймворк, который строится для всех архитектур, включая симулятор и устройство.

Мой ответ охватывает следующие пункты:

  • Сделать фреймворк, который работает как для симулятора, так и для устройства

  • Как экспортировать "толстый" Cocoa Touch Framework (для симулятора и устройства одновременно)?

  • Неопределенные символы для архитектуры x86_64

  • ld: символы не найдены для архитектуры x86_64

Шаги 1: сначала создайте свои фреймворки с помощью симулятора

Шаги 2: После успешного завершения процесса построения симулятора, теперь создайте для своей среды фреймворк с выбором целевого устройства или выбором универсального устройства iOS.

Шаг 3: Теперь выберите цель вашей платформы и для этого в разделе "Этапы сборки" выберите "Добавить сценарий запуска" и скопируйте приведенный ниже код сценария).

Шаг 4: Теперь, наконец, соберите заново, и ваш фреймворк готов к совместимости как с симулятором, так и с устройством. Ура!!!!

[Примечание: мы должны иметь обе совместимые рамки, готовые до последнего шага 4 (совместимы симулятор и архитектура устройства, если нет, пожалуйста, правильно выполните шаги 1 и 2 выше)

Смотрите контрольное изображение:

введите описание изображения здесь

введите описание изображения здесь

Поместите следующий код в область оболочки:

#!/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"

Другие вопросы по тегам