Xcode 6 iOS Создание Cocoa Touch Framework - проблемы с архитектурой
Я пытаюсь сделать динамический фреймворк для приложения для iOS. Благодаря новой версии Xcode (6) мы можем выбрать Cocoa Touch Framework при создании нового проекта, и больше не нужно добавлять совокупную цель, запускать скрипт и так далее, чтобы его создать. У меня нет проблем, когда я создаю структуру. Но когда я пытаюсь использовать его в приложении для iOS, у меня возникают проблемы с архитектурой.
ld: warning: ignoring file /Library/Frameworks/MyFramework.framework/MyFramework, file was built for x86_64 which is not the architecture being linked (arm64): /Library/Frameworks/MyFramework.framework/MyFramework
Undefined symbols for architecture arm64:
"_OBJC_CLASS_$_MyFrameworkWebService", referenced from:
objc-class-ref in AppDelegate.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ld: warning: ignoring file /Library/Frameworks/MyFramework.framework/MyFramework, file was built for x86_64 which is not the architecture being linked (armv7): /Library/Frameworks/MyFramework.framework/MyFramework
Undefined symbols for architecture armv7:
"_OBJC_CLASS_$_MyFrameworkWebService", referenced from:
objc-class-ref in AppDelegate.o
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Ну, я попытался изменить настройки проекта и цели фреймворка (Только архитектуры и построить действительную архитектуру и Действительные архитектуры). Я сделал то же самое для проекта приложения для iOS, но ничего не работает. Я думаю, что есть кое-что, чего я не понял.
Например, когда я создаю фреймворк только для проверки i386 (iOS Simulator) с помощью командной строки "xcrun lipo -info MyFramework", у меня возникает проблема, заключающаяся в том, что
ld: warning: игнорирование файла /Library/Frameworks/MyFramework.framework/MyFramework, файл создан для x86_64, который не является связываемой архитектурой (i386)...
Если кто-то может помочь мне получить фреймворк, который работает для всех архитектур iOS, включая симуляторы.
12 ответов
Извините, что позволил этой теме открыться так долго... Некоторые из вас правильно ответили на мой вопрос. Когда я писал этот вопрос, я не уловил, что в полной (также называемой жирной) структуре (готовой к использованию) было два фрагмента. Один для устройств iOS и один для симулятора. Когда я обнаружил, что я создал скрипт, используя команду lipo, чтобы объединить два среза, я автоматически получил полную структуру.
На основании всех ответов, поста на raywenderlich.com и сути, созданной Chris Conway, я придумал это.
Выполнив следующие шаги, я смог построить инфраструктуру Cocoa Touch (включая файлы Swift и Objective-C), которая содержит все архитектуры как для симулятора, так и для устройства:
- Создайте новую (совокупную) цель в проекте вашей платформы
- Под "Build Phases" выберите "Add Run Script" и скопируйте содержимое этого файла.
- Выберите Aggregate target в раскрывающемся списке " Выбор схемы"
- Построить цель для агрегированной схемы
Надеюсь, поможет:)
ОБНОВЛЕНИЕ: Исправлена ошибка в гисте, где пути на шаге № 3 были неправильными. Спасибо Tokuriku!!
ОБНОВЛЕНИЕ В Xcode 7 и 8 нажмите File>New>Target... и там выберите "Other" группу, чтобы выбрать Aggregate target
Попробуйте выполнить следующие шаги, чтобы создать рабочую область, которая содержит каркасный проект и проект приложения.
Рабочая среда:
- Создать рабочее пространство.
Рамочный проект:
- Создайте проект iOS Cocoa touch Framework в рабочей области.
- Добавьте простой класс Objective C MyClass (заголовок.h и файл реализации.m) и создайте метод - (void) приветствия.
- Перейдите к проекту Build Phases > Headers > Переместите MyClass.h из Project в Public.
- Измените схему на проект фреймворка и выберите симулятор iOS, затем соберите. (Выберите iOS Device, если приложение, которое интегрирует эту среду, работает на устройстве. Я продолжу использовать симулятор в этом примере)
- У вас не должно быть проблем с сборкой, сборка фреймворка находится в вашем каталоге Derived Data, который вы можете найти в Organizer.
Проект приложения:
- Создайте приложение Swift Single View внутри рабочей области.
- Перетащите над сборкой каркаса симулятора iOS (находится в Debug-iphonesimulator или Release-iphonesimulator) в ваш проект.
- Создайте соединительный заголовок, чтобы выставить методы класса Objective C в Swift.
- Импортируйте MyClass.h в файл заголовка моста.
- Обратите внимание, что если определение MyClass не найдено, то добавьте путь заголовков фреймворка в пути поиска заголовка настроек сборки.
- Создайте экземпляр MyClass в viewDidLoad ViewController.swift, затем вызовите приветствия.
- Добавить фреймворк в Target > Embedded Binaries
- Измените схему на проект приложения и выберите симулятор iOS, затем выполните сборку.
- Вы должны видеть приветственные сообщения.
Обратите внимание, что в приведенном выше примере показано, как создать приложение, которое запускается в симуляторе. Если вам нужно создать универсальную статическую библиотеку, которая будет работать как на симуляторе, так и на устройствах, то общие шаги:
- Сборка библиотеки для симулятора
- Сборка библиотеки для устройства
- Объедините их, используя липо
В Интернете есть хорошие ссылки на это, например, здесь.
Создайте универсальный бинарный файл для фреймворка: перейдите в каталог производных данных фреймворка, затем / Build / Products, следующая команда должна помочь вам создать универсальный бинарный файл в каталоге Products:
lipo -create -output "framework-test-01-universal" "Debug-iphonesimulator/framework-test-01.framework/framework-test-01" "Debug-iphoneos/framework-test-01.framework/framework-test-01"
Обратите внимание, что framework-test-01 - это имя моего фреймворкового проекта.
Вот шаги для создания толстой (универсальной) платформы:
Задавать
ONLY_ACTIVE_ARCH=NO
вBuild Settings
в вашем проекте.- Сборка библиотеки для симулятора
- Сборка библиотеки для устройства
Открыть в консоли
Products
папку для вашего фреймворка (вы можете открыть его, открыв папку фреймворка иcd ..
оттуда)
Запустите этот скрипт из
Products
папка. Это создает толстый Framework в этой папке. (или сделайте это вручную, как описано ниже в 4. 5.)framework_name="${$(basename $(find ./Debug-iphoneos -type d -name '*.framework' -maxdepth 1))%.*}" && \ cp -R Debug-iphoneos/$framework_name.framework ./$framework_name.framework && \ lipo -create -output "$framework_name.framework/$framework_name" "Debug-iphonesimulator/$framework_name.framework/$framework_name" "Debug-iphoneos/$framework_name.framework/$framework_name"
Или же:
Объедините эти 2 фреймворка, используя липосакцию этим скриптом (замените
YourFrameworkName
на имя вашего фреймворка)lipo -create -output "YourFrameworkName" "Debug-iphonesimulator/YourFrameworkName.framework/YourFrameworkName" "Debug-iphoneos/YourFrameworkName.framework/YourFrameworkName"
Замените новым двоичным файлом одну из существующих платформ:
cp -R Debug-iphoneos/YourFrameworkName.framework ./YourFrameworkName.framework mv YourFrameworkName ./YourFrameworkName.framework/YourFrameworkName
- Прибыль:
./YourFrameworkName.framework
- готовый к использованию жирный бинарный файл! Вы можете импортировать его в свой проект!
Для проекта, который не в xcworkspace
-es:
Вы также можете попытаться использовать эту суть. Но кажется, что она не работает для проектов в рабочих пространствах.
То, как я это сделал, похоже на vladof, но, надеюсь, немного проще. Я сделал фреймворк подпроектом проекта приложения.
Рамочный проект
- Создайте новую iOS Cocoa Touch Framework. Назовите это MyLib. Это создаст один MyLib.h
- Добавьте простой класс Cocoa Touch Obj-C MyClass (.h & .m) и в реализации.m создайте метод, который возвращает строку, - (NSString *)greetings;
- В MyLib.h добавьте это внизу, #import "MyClass.h"
- В разделе "Этапы сборки / заголовки" цели переместите MyClass.h из раздела "Проект" в раздел "Открытый".
- Сделать сборку (cmd-B)
- Закройте проект
Проект приложения
- Создайте новый проект приложения Single View, Swift или Obj-C. Назовите это MyApp.
- Из Finder перетащите файл проекта MyLib в левую секцию органайзера окна MyApp и убедитесь, что линия вставки находится чуть ниже MyApp. Это делает MyLib подпроектом MyApp. (Он может использоваться таким же образом в других проектах)
- Нажмите на MyApp в органайзере, затем выберите цель MyApp и выберите Build Phases.
- В разделе "Целевые зависимости" нажмите знак "+" и добавьте MyLib.
- В разделе "Связать с библиотеками" нажмите знак "+" и добавьте MyLib.framework.
Для приложения Obj-C
- В ViewController.m добавьте #import
- В viewDidLoad добавьте следующие строки:
- MyLib * x = [[MyLib alloc] init];
- NSLog (@ "% @", x.greetings);
- Запустите проект, и вы должны увидеть сообщение в окне отладки. -
Для приложения Swift
- В ViewController.swift добавьте импорт MyLib
- в viewDidLoad добавьте следующие строки:
- var x: MyLib = MyLib ()
- println ("(x.greetings ())") -
Делая это таким образом, приложение зависит от фреймворка, поэтому, если вы вносите изменения в классы фреймворка, вам не нужно менять цели, чтобы построить фреймворк отдельно, сначала будет скомпилирована фреймворк, а затем приложение.
Я немного изменил чей-то скрипт для поддержки всех архитектур симулятора:
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}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator VALID_ARCHS="x86_64 i386" BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
# Step 2. Copy the framework structure to the universal folder
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"
# Step 3. 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 4. Convenience step to copy the framework to the project's directory
cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"
# Step 5. Delete temporary build directory in the project's directory
rm -rf "${PROJECT_DIR}/build"
# Step 6. Convenience step to open the project's directory in Finder
open "${PROJECT_DIR}"
Старое видео, которое я сделал, так как ссылка выше, больше не доступно.
Этот вопрос был опубликован некоторое время назад (Xcode 6), но я недавно столкнулся с той же проблемой с Xcode 10.
Итак, проблема в том, что встроенный фреймворк не поддерживает достаточно архитектур.
Если вы не знаете, что такое архитектура, разные устройства iPhone имеют разную архитектуру, вот полный список:
- arm6: iPhone 1, 2, 3G
- arm7: используется в самых старых устройствах с поддержкой iOS 7 [32 бита]. iPhone 4, 4S
- arm7s: используется в iPhone 5 и 5C[32 бита]. iPhone 5, 5с
- arm64: для 64-битного процессора ARM в iPhone 5S[64 бит]. iPhone 5s и выше.
- arm64e: используется на чипсете A12. iPhone XS / XS Max / XR
- i386: для 32-битного симулятора
- x86_64: используется в 64-битном симуляторе
Итак, если вы используете фреймворк на симуляторе, фреймворк должен поддерживать либо i386, либо x86_64; если вы запускаете приложение на iPhone 6, фреймворк должен поддерживать архитектуру arm64.
Поэтому в большинстве случаев фреймворк должен поддерживать все вышеупомянутые архитектуры.
Теперь вернемся к тому, как решить проблему. Нам нужно построить каркас как для устройств, так и для симуляторов.
Как построить для устройств:
- В поле "Общие" укажите "Цель развертывания".
- В "Настройках сборки" установите "Только для активных архитектур" на "Нет".
- В "Настройках сборки" убедитесь, что в "Действительных архитектурах" перечислены все необходимые архитектуры. Обычно мы просто используем параметры по умолчанию (arm64, arm64e, armv7, armv7s).
- Перейдите в "Редактировать схему" -> "Выполнить" -> "Конфигурация сборки", измените "Конфигурация сборки" на "Выпуск"
- Выберите активную схему "Универсальное устройство iOS". Нажмите Cmd+B, чтобы построить проект.
- Щелкните правой кнопкой мыши [MyFrameworkProject].framework в папке "Products" в вашем проекте Xcode и нажмите "Показать в Finder".[MyFrameworkProject].framework поддерживает все архитектуры, указанные в шаге 2.
- Перетащите [MyFrameworkProject].framework в проект, который должен использовать эту платформу. Кроме того, перетащите [MyFrameworkProject].framework в "Встроенные двоичные файлы".
Как построить для симуляторов:
- Шаг с 1 по 4 такой же, как указано выше.
- Выберите любой симулятор в качестве активной схемы. Нажмите Cmd+B, чтобы построить проект.
- Шаг 6-7 такой же, как указано выше.
Как построить для устройств и симуляторов:
После того, как у вас есть фреймворк для устройств, вам нужно будет объединить фреймворк для симуляторов и фреймворк для устройств вместе с командой "lipo". Переименуйте каркас для симуляторов в [MyFrameworkProject]_sim.framework и скопируйте оба каркаса в одну папку. Запустите команду ниже в Терминале (убедитесь, что вы находитесь в папке):
lipo -create -output [MyFrameworkProject].framework/[MyFrameworkProject] [MyFrameworkProject].framework/MyFrameworkProject [MyFrameworkProject]_sim.framework/MyFrameworkProject
Теперь [MyFrameworkProject].framework является конечным продуктом, который поддерживает как симуляторы, так и устройства.
Наконец-то у меня все получилось! И извините за большую желтую рамку, я понятия не имею, как лучше отформатировать ее.
Решение пришло от Клаудио Романди, но в связанном скрипте есть небольшая проблема. Я не могу комментировать его пост, потому что мне нужно 50 репутации, поэтому у меня не осталось иного выбора, кроме как скопировать его пост, чтобы получить полное решение..
- Создайте новую (совокупную) цель в вашем рамочном проекте
- Выберите Aggregate в рабочей области и добавьте "Фаза нового запуска сценария"
Поместите в него следующий код:
#!/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) to the copied framework directory cp -R "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/." "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule" # 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 "${PROJECT_DIR}"
Выберите агрегат в раскрывающемся списке выбора схемы
- Построй, все готово!
Проблема заключалась в том, что каталог симулятора указывал на несуществующий каталог, меняя "Framework" на "${PROJECT_NAME}" в 2 местах, и все получалось:)
Несколько дополнительных моментов вокруг подхода, разделяемого vladof, которые более применимы к быстрым основам
- Сценарий в соответствующей ссылке нуждается в модификации для копирования всех файлов с iphoneimulator и iphoneos, поскольку swift имеет отдельные скомпилированные файлы для arm и i386
- Убедитесь, что у вас есть ARCHS="x86_64" ONLY_ACTIVE_ARCH=NO в сборке для iphonesimulator, чтобы избежать проблем компоновщика для симулятора
- Убедитесь, что ваш интерфейс / класс расширяет NSObject, иначе вы столкнетесь с проблемами при попытке использовать код в swift (он будет жаловаться на невозможность создания объекта с помощью ().
Хотелось добавить что-нибудь к ответу сценария липосакции, предоставленному Skywinder здесь. Я следовал за шагами, но все еще не мог заставить мою структуру работать на симуляторе, только устройстве. Чтобы это исправить:
- Я вошел в версию фреймворка debug-iphonesimulator и в Modules/[FrameworkName].swiftmodule скопировал все файлы i386 и X86.
Затем я вошел во вновь созданную толстую версию фреймворка и перешел в ту же папку. Я вставил в файлы i386 и X86 (чтобы уже там были файлы ARM), а затем добавил свой толстый фреймворк в свой проект.
Вуаля, она работает на симуляторе и реальном устройстве! Это Xcode 10 / Swift 4.2, кстати