Привет, Хабр! Я Николай Чурянин, начальник отдела разработки приложений для юридических лиц. Как-то нам в голову пришла мысль подключать зависимости в виде собранных библиотек и тем самым не тратить время на пересборку редко изменяемых зависимостей. В данной статье рассмотрим, как создавать XCFramework из SPM пакета.

Обратимся к документации, как Apple рекомендует создавать бинарный артефакт.

Для начала в настройках проекта нужно установить следующие значения для переменных окружения: 

  • SKIP_INSTALL=NO, чтобы продукт был включен в архив,

  • BUILD_LIBRARY_FOR_DISTRIBUTION=YES, чтобы был сгенерирован интерфейс продукта.

И выполнить команду для создания архива:

xcodebuild archive
    -project MyFramework.xcodeproj
    -scheme MyFramework
    -destination "generic/platform=iOS"
    -archivePath "archives/MyFramework"

Сразу за ней — команду сборки xcframework’а:

xcodebuild -create-xcframework
    -archive archives/MyFramework-iOS.xcarchive -framework MyFramework.framework
    -archive archives/MyFramework-iOS_Simulator.xcarchive -framework MyFramework.framework
    -archive archives/MyFramework-macOS.xcarchive -framework MyFramework.framework
    -archive archives/MyFramework-Mac_Catalyst.xcarchive -framework MyFramework.framework
    -output xcframeworks/MyFramework.xcframework

Примеры из документации.

Попробуем то же самое сделать для SPM пакета. Создадим новый пакет MyFramework. Указываем тип библиотеки .dynamic, т.к. для создания необходим .framework.

 Package.swift

let package = Package(
    name: "MyFramework",
    products: [
        .library(
            name: "MyFramework",
            type: .dynamic,
            targets: ["MyFramework"]),
    ],
    dependencies: [
    ],
    targets: [
        .target(
            name: "MyFramework",
            dependencies: []),
        .testTarget(
            name: "MyFrameworkTests",
            dependencies: ["MyFramework"]),
    ]
)

Вызовем команду создания xcarchive:

xcodebuild archive
    -scheme MyFramework
    -destination "generic/platform=iOS"
    -archivePath "archives/MyFramework.xcarchive"
    SKIP_INSTALL=NO
    BUILD_LIBRARY_FOR_DISTRIBUTION=YES

И следом команду создания xcframework:

xcodebuild -create-xcframework
    -archive "archives/MyFramework.xcarchive"
    -framework MyFramework.framework
    -output xcframeworks/MyFramework.xcframework

В результате выполнения последней команды получаем следующую ошибку:

error: the path does not point to a valid framework: /archives/MyFramework.xcarchive/Products/Library/Frameworks/MyFramework.framework

Данная ошибка означает, что по искомому пути нет MyFramework.framework. Попробуем разобраться. Для этого посмотрим, что находится внутри созданного MyFramework.xcarchive:

А внутри MyFramework.framework по искомому пути действительно нет, ошибка вполне логичная.

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

Во-первых, нам необходимо изменить путь установки .framework. Для этого необходимо указать каталог, в который помещается продукт внутри архива, в переменную окружения INSTALL_PATH:

INSTALL_PATH=/Library/Frameworks

В результате .framework находится там, где и ожидает команда сборки xcframework, и, как следствие, бинарный артефакт успешно создаётся:

xcframework successfully written out to: /xcframework/MyFramework.xcframework

Но вот при использовании получаем следующую ошибку:

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

Для исправления ситуации установим тип продукта и каталог, в который будет помещен интерфейс, в следующие переменные окружения соответственно:

PRODUCT_TYPE=com.apple.product-type.framework MODULES_FOLDER_PATH=MyFramework.framework/Modules

В результате архив содержит .swiftmodule. При использовании xcframework, созданного на основе такого архива, ошибок уже нет.

Укажем также необходимость добавить в архив заголовочный файл, а также каталог, в котором данный файл будет находиться, в следующие переменные окружения соответственно:

SWIFT_INSTALL_OBJC_HEADER=YESPUBLIC_HEADERS_FOLDER_PATH=MyFramework.framework/Headers    

Команда для создания архива в результате выглядит следующим образом:

xcodebuild archive
    -scheme MyFramework
    -destination "generic/platform=iOS"
    -archivePath "archives/MyFramework.xcarchive"
    SKIP_INSTALL=NO
    BUILD_LIBRARY_FOR_DISTRIBUTION=YES
    INSTALL_PATH=/Library/Frameworks
    MODULES_FOLDER_PATH=MyFramework.framework/Modules
    PRODUCT_TYPE=com.apple.product-type.framework
    PUBLIC_HEADERS_FOLDER_PATH=MyFramework.framework/Headers
    SWIFT_INSTALL_OBJC_HEADER=YES

Итак, создание XCFramework из SPM пакета по своей сути ничем не отличается от создания из xcodeproj. Тем не менее нужно погрузиться глубже, чтобы понять, как это сделать. Например, тут реализован внушительных размеров скрипт для выполнения данной задачи.

Прежде чем придумывать своё решение, лучше попробовать разобраться в существующих инструментах. Если есть желание покопаться в данной теме, можно попробовать собрать XCFramework из SPM пакета с ресурсами.

Комментарии (3)


  1. Krypt
    16.08.2024 19:45
    +1

    У меня была такая задача (как возможное решение существовавшей проблемы), и я не уверен, что я сделал все описанные тут шаги, но проблема с которой я столкнулся и не смог побороть - в фреймворк как раз таки не были включены ресурсы.
    Насколько я понимаю это решение так же не включит ресурсы?
    Пакет, который я пытался собрать - AppMetrica


    1. nchuryanin Автор
      16.08.2024 19:45
      +1

      Да, представленная команда ресурсы не включит в xcframework.

      Предлагаю желающим покопаться в данной теме самим попробовать разобраться, основываясь на данной статье, как добиться включения и ресурсов в xcframework.

      Позднее уделю внимание данному вопросу.


      1. Krypt
        16.08.2024 19:45

        Хах! Вы мне сейчсас моего учителя из универа напомнили: он говорил: что если в книге написанно "это тривиально доказать" или "оставляю как упражнение читателю" - автор сам не знает доказательства :D

        Когда я занимался этой задачей (подключить в проект собираемый Bazel'ом обновлённую версию AppMetrica) - всё, что я смог найти в интернете: так это что никто ещё не разобрался как это сделать. Я в итоге я просто стал патчить исходники, чтобы существующие инструменты могли собрать их. (Яндекс, фу, изменять расшинения заголовочных файлов с .h на .def - плохая идея)