Swift Package Manager in 2021
Вопросы
- Почему стоит уйти с Cocoapods?
- Как интегрировать пакеты? Что со сборкой на CI?
- Как структурировать пакеты в SwiftPM
- Как удобно разрабатывать отдельный пакет
- Как удобно выделять модуль для приложением
- Как выпускать версии пакетов и библиотек
Почему стоит уйти с Cocoapods
SwiftPM поддерживает всё, что поддерживает Cocoapods и даже чуть больше.
Поддержка ресурсов
Работает, начиная с Xcode 12 и Swift 5.3.
Ресурсы не обязательно объявлять в Package.swift файле. Например, asset catalogs, xib/storyboard, .lproj файлы будут включены в Bundle автоматически.
When you add a resource to your Swift package, Xcode detects common resource types for Apple platforms and treats them as a resource automatically. For example, you don’t need to make changes to your package manifest for the following resources:
- Interface Builder files; for example, XIB files and storyboards
- Core Data files; for example, xcdatamodeld files
- Asset catalogs
- .lproj folders you use to provide localized resources
Использовать ресурсы в коде стало проще. Внутри Swift Package Manager пакета мы можем написать просто Bundle.module.
UIImage(named: "some_image", bundle: .module)
Это работает благодаря флагу SWIFT_PACKAGE, который доступен внутри пакета.
Пример добавления ресурсов описан в статье useyourloaf.
Подробнее в видео WWDC20. Swift packages: Resources and localization
SwiftGen, R.swift
Работает через Bundle.module.
Нет явных шагов для билда ресурсов, как и с обычными пакетами. Кажется, имеет смысл держать рядом папку scripts с cli. И запускать периодически в терминале.
Умеет в версионирование
- Выбор версии до следующей мажорной (95% использования)
- Конкретная версия
- Конкретная ветка
Поддерживает binary frameworks
Поддержка xcframework нужна для библиотек, у которых мы хотим закрыть исходный код. Или, например, у KMM библиотеки, которая нужна как зависимость.
Такие фреймворки ограничены только Apple платформой и теми архитектурами, под которые они собраны. Это может вызывать проблемы при разработке.
Подробнее об этих фреймоврках и интеграции в Swift Package Manager в видео Distribute binary frameworks as Swift Packages и документации о Binary Frameworks
Интеграция пакетов и сборка на CI
Внешние пакеты внутри приложения
Пакеты можно добавлять через File > Swift Packages. Поиск по url пакета. Пакет скачается и попробует построить дерево совместимости.

swift package умеет создавать, собирать и запускать модули. Поставляется вместе с Xcode.
На CI пакеты резолвятся автоматически при начале сборки, и дополнительных действий делать не нужно.
Модульное приложение на Swift Package Manager
Модули выделять стало намного проще. File > New > Swift Package. Добавляя его в репозиторий и проект - появляется понятная структура. Она повторяет файлы внутри, в отличие от обычных проектов. Генерируется папка тестов, автоматом считывается Scheme для билда и тестирования пакета. Зависимости такого модуля можно объявить в Package.swift
Структура пакетов
1 репозиторий = коллекция пакетов.
Executable — cli. Удобно разрабатывать с любыми пакетами, которые тоже поддерживают SwiftPM. swift-argument-parser — мощь.
К сожалению, использовать с SPM SwiftGen/R.swift и любые другие CLI, как это было с Cocoapods - не получится. Валидные решения такие:
- положить бинарь с утилитой в код проекта и запускать внутри. Будет не очень удобно обновляться, но все остальные удобства останутся.
- использовать Brewfile/Gemfile для контроля зависимостей. Будет удобно обновлять, но нужно запускать перед билдом brew install/bundle install
Library — библиотека. Может быть как статической, так и динамической.
Как удобно разрабатывать
Файловая структура пакета
Внутри репозитория пакета можно собрать такую иерархию файлов:
- 📁 Project (git repo)
- 📄 Package.swift
- 📄 Package.resolved
- 📄 README.md
- 📁 Sources
- 📁 Helpers
- 📁 BigModule
- 📁 Tests
- 📁 BigModuleTests (folder)
- 📁 Example
- Example.xcodeproj
- … any structure of example project
- 📄 Packages.xcworkspace
- Packages.playground (еще не пробовал, но должно тоже работать)
xcworkspace файл объединит в себе две группы
| |
В таком случае xcworkspace повторяет всю структуру папок, при этом имея все схемы для сборки пакетов + схему для сборки Example проекта.
К сожалению, иногда приходится закрыть-открыть workspace. Обычно это проблемы со схемами или резолвом пакетов.
Kontur: мы используем такую иерархию в packages-ios.
Package.swift
// swift-tools-version:5.3 - актуальная версия тулзов. Актуальная фича в этой версии - process/copy ресурсов.
API Reference swift.org — место, где можно посмотреть документацию к каждому вызову внутри Package.swift
Про отличие static и dynamic библиотек можно почитать в блоге theswiftdev.
| |
Релизы
Релизы легче, чем в cocoapods, так как централизованного хранилища версий необходимости держать нет. Просто добавляем тег версии по semver, и пакеты готовы к обновлению.
Если хочется выложить пакет в общий доступ и позволить пользователям искать его в общем списке — посмотри на Swift Package Index.
Как решать проблемы
Пакеты начинают резолвиться при открытии Xcode проекта. Если удалить пакеты, то они пропадут и билд не будет их видеть. Тогда нужно воспользоваться меню File > Swift Packages > Resolve Package Versions
Проблемы кэширования решаются удалением кэша 😀 Ссылка на него лежит в ~/.swiftpm/cache. Оригинал лежит в ~/Library/Caches/org.swift.swiftpm. Рядом с кэшем Cocoapods.
Проблема совместимости с тем, что модули не подходят друг другу решается вручную. Как, например, если мы поставим RxSwift 6.0.0+ и последнюю версию Moya, которая работает с RxSwift 5.0.0+. Тогда нужно явно указать, что версия RxSwift необходима старая. Автоматически это не решится, но лог пишется понятный.

Проблема с разными версиями одного пакета
| |
Не вижу возможного решения в текущей версии, так как Package.swift нельзя выделить отдельно. Думал о том, чтобы Quick с версией другой имел название библиотеки вроде Quick-4, и тогда бы не должна была ломаться связка. Но как такое решить с текущей интеграцией Xcode - не понятно.
Валидные решения на сейчас:
- выпилить Quick из тестов :)
- обновить Moya в отдельном репозитории. Версия 15 с поддержкой RxSwift давно готова. Но её не выпускают непонятно почему. Скорее всего, проблема в документации.
Полезные ссылки
Документация по API SwiftPM в Package.swift файле
Публичный список доступных пакетов в SwiftPM
UPD: 12.2021
Всё ещё очень долго происходит загрузка пакетов при открытии проекта в Xcode. Если подряд открыть проект несколько раз — то и загрузка будет происходить каждый из этих раз. Это может занимать очень много времени. В Cocoapods же можно было не обновлять и не подтягивать каждый раз пакеты.