Ответ
Преимущества:
- Нативная интеграция: Встроен прямо в Xcode и Swift, не требует установки дополнительных инструментов.
- Декларативная конфигурация: Все зависимости и цели проекта описываются в одном файле
Package.swiftна языке Swift. - Поддержка семантического версионирования: Позволяет точно указывать диапазоны версий (
.upToNextMajor,.exact). - Управление зависимостями на уровне продукта: Пакет может объявлять несколько библиотечных или исполняемых продуктов.
- Кроссплатформенность и поддержка серверного Swift: Работает на всех платформах, где есть Swift.
Недостатки и ограничения:
- Ограниченная поддержка бинарных зависимостей: Хотя бинарные цели (
binaryTarget) существуют, их использование менее удобно, чем в CocoaPods (например, для закрытых бинарных SDK). - Отсутствие центрального репозитория: Нет аналога CocoaPods Trunk, что усложняет поиск пакетов (хотя интеграция с GitHub и другими Git-хостами хорошая).
- Сложности с ресурсами для iOS/macOS: Поддержка ресурсов (изображений, xib, storyboard) появилась позже и может требовать дополнительной настройки.
- Нет встроенного кэширования зависимостей: При отсутствии локального клона пакет загружается заново, в отличие от CocoaPods, который кэширует исходники в
~/.cocoapods.
Пример файла Package.swift:
// swift-tools-version:5.9
import PackageDescription
let package = Package(
name: "MyLibrary",
platforms: [
.iOS(.v15),
.macOS(.v12)
],
products: [
.library(
name: "MyLibrary",
targets: ["MyLibrary"]),
.executable(
name: "MyTool",
targets: ["MyTool"]),
],
dependencies: [
// Зависимость из репозитория Git с диапазоном версий
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.8.0"),
// Зависимость из конкретной ветки
.package(url: "https://github.com/ReactiveX/RxSwift.git", branch: "main"),
// Зависимость из локального пути (для разработки)
.package(path: "../MyLocalPackage"),
// Зависимость с точной версией
.package(url: "https://github.com/SnapKit/SnapKit.git", exact: "5.6.0")
],
targets: [
.target(
name: "MyLibrary",
dependencies: [
.product(name: "Alamofire", package: "Alamofire"),
"RxSwift", // Имя пакета может использоваться как имя зависимости, если оно совпадает
.product(name: "RxCocoa", package: "RxSwift")
],
resources: [.process("Resources")] // Включение ресурсов
),
.executableTarget(
name: "MyTool",
dependencies: ["MyLibrary"]
),
.testTarget(
name: "MyLibraryTests",
dependencies: ["MyLibrary", "SnapKit"] // Зависимость только для тестов
),
// Бинарная цель (XCFramework)
.binaryTarget(
name: "MyBinarySDK",
url: "https://example.com/MyBinarySDK.xcframework.zip",
checksum: "abc123..."
)
]
) Ответ 18+ 🔞
А, ну ты про Swift Package Manager, да? Эта штука, которая в Xcode встроена, типа как родной инструмент. Ну, вроде как удобно, не надо никаких CocoaPods отдельно ставить, всё прямо в среде разработки. Но, блядь, как всегда, есть свои подводные камни, о которых тебе никто не расскажет, пока сам не наступишь.
Что там хорошего, говоришь?
- Встроен прямо в Xcode. Ну да, открыл, добавил пакет по URL — и вроде как работает. Не надо
pod installкаждый раз бегать делать, что уже приятно. - Конфиг на Swift. Файлик
Package.swiftпишешь на том же языке, на котором и код. Вроде как логично, не надо учить какой-то свой, ёбаный, Ruby DSL, как в тех же Pod'ах. - Версии. Тут всё чётко: можешь сказать «дай мне любую версию, но только до следующего мажора», или «только вот эту конкретную, нахуй». Удобно.
- Не только библиотеки. Можешь в одном пакете и библиотеку описать, и какой-нибудь исполняемый инструмент — типа всё в одном флаконе.
- Кроссплатформенность. Ну, Swift же. Работает везде, где он есть. Для серверных штук — вообще красота.
А теперь, сука, ложка дёгтя, которая всегда найдётся:
- Бинарники — боль. Хоть и есть
binaryTarget, но попробуй-ка подключи закрытый SDK в виде.xcframework. В CocoaPods это было проще, честно. Тут тебе и checksum надо высчитывать, и если обновили SDK — всё, танцы с бубном. - Где искать-то? Нету центральной базы, как CocoaPods Trunk. Сидишь, гадаешь на кофейной гуще: «А есть ли пакет для вот этой задачи на GitHub?» Приходится гуглить, как последний лох.
- Ресурсы. О, это отдельная песня. Раньше их вообще не было. Сейчас вроде есть, но если тебе надо картинки, xib'ы или storyboard запихнуть — готовься к тому, что может не собраться с первого раза. Настраивай, блядь,
resourcesв таргете. - Кэширование — хуёвание. Нет локального кэша, как у Pod'ов в
~/.cocoapods. Удалил пакет из проекта — в следующий раз будет качать заново всё, особенно если интернет тормозной. Веселье.
Вот, смотри, как этот Package.swift выглядит, чтоб ты понимал масштаб:
// swift-tools-version:5.9
import PackageDescription
let package = Package(
name: "MyLibrary",
platforms: [
.iOS(.v15),
.macOS(.v12)
],
products: [
.library(
name: "MyLibrary",
targets: ["MyLibrary"]),
.executable(
name: "MyTool",
targets: ["MyTool"]),
],
dependencies: [
// Взял с GitHub, версию любую, но с пятой мажорной
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.8.0"),
// А эту либу тянем прямо с ветки main (живём на грани, да)
.package(url: "https://github.com/ReactiveX/RxSwift.git", branch: "main"),
// А это сосед по папке, локальный пакет для тестов
.package(path: "../MyLocalPackage"),
// А вот эту — только точную версию, ни шагу в сторону!
.package(url: "https://github.com/SnapKit/SnapKit.git", exact: "5.6.0")
],
targets: [
.target(
name: "MyLibrary",
dependencies: [
.product(name: "Alamofire", package: "Alamofire"),
"RxSwift", // Если имя пакета и продукта совпадают — можно так
.product(name: "RxCocoa", package: "RxSwift")
],
resources: [.process("Resources")] // Вот тут ресурсы наши, которые могут не загрузиться
),
.executableTarget(
name: "MyTool",
dependencies: ["MyLibrary"]
),
.testTarget(
name: "MyLibraryTests",
dependencies: ["MyLibrary", "SnapKit"] // SnapKit только для тестов, например
),
// А вот и бинарник, с которым может быть боль
.binaryTarget(
name: "MyBinarySDK",
url: "https://example.com/MyBinarySDK.xcframework.zip",
checksum: "abc123..." // Забудешь обновить — проект не соберётся, пиздец
)
]
)
В общем, инструмент в целом адекватный, особенно для чистого Swift. Но если ты привык к Pod'ам и у тебя проект с кучей легаси и бинарных зависимостей — готовься к волшебству и непредсказуемости. Иногда кажется, что он просто издевается.