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

107

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

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

Из-за:

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

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

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

Но я не нашел никаких руководств, как экспортировать универсальный Fat 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, создавая какао Touch Framework - проблемы с архитектурой, но он не дублируется.


Обновить:

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

Skywinder
источник
дублированная проблема stackoverflow.com/questions/24039470/…
Андрюс Степонавичюс
@ AndriusSteponavičius этот вопрос был задан 2 месяца назад.
Skywinder 02
Да, но там есть гораздо более подробные ответы, о которых, я думаю, пользователи должны знать
Андрюс Степонавичюс
Установите ONLY_ACTIVE_ARCH = NO в настройках сборки - это важный шаг.
Jedidja
ваша структура нуждается в обоих срезах i386 x86_64 в толстом двоичном файле, если вы хотите запустить его на симуляторе, ДАЖЕ ЕСЛИ ВАШ КОМПЬЮТЕР ИМЕЕТ 64-БИТНУЮ АРХИТЕКТУРУ !!! Узнал это на собственном горьком опыте.
J.beenie

Ответы:

82

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

TL; DR;

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

Далее следует более длинный ответ


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

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

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

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

В настоящее время единственный способ - это именно то, что вы сделали:

  • Создайте цель как для симулятора, так и для устройства iOS.
  • Перейдите в папку Xcode DerivedData для этого проекта и объедините два двоичных файла в одну единую структуру. Однако при создании целевой платформы в Xcode убедитесь, что для параметра цели «Только сборка активной архитектуры» установлено значение «НЕТ». Это позволит Xcode создать цель для нескольких типов binarty (arm64, armv7 и т. Д.). Вот почему он работает из Xcode, но не как отдельный двоичный файл.

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

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

lipo -info MyFrameworkBinary

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

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

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

Вот как Carthage использует липо, а Realm - липо .


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

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

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

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

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

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

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

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

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


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

Станислав Панкевич
источник
1
Я использовал липо, но когда фреймворк строится в симуляторе, он показывает неразрешенный идентификатор с именем класса, но на устройстве он работает. Если использовать бинарную версию симулятора, то она работает .. любая идея?
Susim Samanta
2
Я не нашел никаких доказательств того, что Xcode 8.2 изменил это в декабре 2016 года.: /
Джеффри Уайзман
1
@Geoffrey, это изменилось в Xcode 9.2 или что-то другое? Я впервые создаю двоичную структуру для распространения, и мне уже страшно ...
ScottyB
К сожалению, немного не сделал этого - не могу сказать. Удачи.
Джеффри Уайзман,
57

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

  1. Установить ONLY_ACTIVE_ARCH=NOвBuild Settings

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

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

  1. Запустите этот скрипт из Productsпапки. Он создает в этой папке fat 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- это готовый толстый бинарник! Вы можете импортировать его в свой проект!

Для проекта, которого нет в Рабочих областях:

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

Skywinder
источник
Я думал, что Apple больше не принимает толстые двоичные файлы. kodmunki.wordpress.com/2015/03/04/…
Monstieur
1
@skywinder Вы нашли какой-либо другой простой способ экспортировать Cocoa Touch Framework в готовый к использованию двоичный файл? Я использую тот же подход, что и выше, но мне он не нравится. В Xcode должно быть что-то, что автоматизирует процесс.
dev gr
1
@devgr еще нет .. поэтому я не принял свой ответ. Все еще ищу лучшее решение.
Skywinder
1
Невозможно запустить в симуляторе, но работает в устройстве с 3-мя и 4-мя ступенями
jose920405
1
@ может ли кто-нибудь объяснить, почему используется только Debug-папка lipo -create? Можно ли использовать этот фреймворк для Releaseнастройки и почему? Спасибо.
Евгений Дубинин
10

Ответ @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для создания толстого фреймворка, ни для Realm, strip-frameworks.shчтобы удалить ненужные фрагменты при отправке в App Store.

odm
источник
На какой из них вы ссылаетесь?
Яка Янчар
@ JakaJančar Я ссылаюсь на те, что в ${SRCROOT}/Frameworks/Activeпапке. Они заменяются правильными предварительно скомпилированными фреймворками для активной архитектуры во время компиляции.
odm
2
Любить это! Это намного проще, чем lipoподход «объединить, а затем разорвать» .
Clozach
2

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

  1. Создайте основу касания какао.
  2. Установите для битового кода включенное значение Нет.
  3. Выберите цель и выберите схемы редактирования. Выберите «Выполнить» и выберите «Выпуск» на вкладке «Информация».
  4. Никаких других настроек не требуется.
  5. Теперь создайте основу для любого симулятора, поскольку симулятор работает на архитектуре x86.
  6. Щелкните группу «Продукты» в «Навигаторе проекта» и найдите файл .framework.
  7. Щелкните его правой кнопкой мыши и выберите Показать в поисковике. Скопируйте и вставьте его в любую папку, я лично предпочитаю название «симулятор».
  8. Теперь создайте платформу для Generic iOS Device и выполните шаги с 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, присутствующего внутри MyFramework.framework. Мы получаем универсальный фреймворк, который подходит для всех архитектур, включая симулятор и устройство.

Nrip
источник
Я хочу создать файл FAT с включенным битовым кодом. Пожалуйста, направь меня.
user3898700
2

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

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

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

  • Как экспортировать «толстый» Cocoa Touch Framework (для Simulator и Device)?

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

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

Шаги 1. Сначала создайте свои фреймворки с помощью Simulator target

Шаги 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"

Сандип Патель - SM
источник
Кажется, что этот скрипт вызывает сам себя и вызывает бесконечный цикл !! Пришлось перезагрузить компьютер после его запуска! Постоянно порождает новые процессы xcodebuild ... и открывает новые окна поиска - проголосовал бы против
J.beenie
Посмотрите ответ @ l0gg3r в этом SO Q / A для аналогичного сценария без проблемы с рекурсией.
J.beenie