Cocoapods считается наиболее популярным менеджером зависимостей для iOS. Последние годы Apple работает над развитием своего нативного менеджера зависимостей Swift Package Manager (SPM).
Изначально его использование было возможно только для server-side Swift или приложений для терминала. На таких приложениях обкатывали и дорабатывали SPM, комьюнити знакомилось с его работой, а команда Apple получила бета тестеров.
С релизом Xcode 11 SPM начал приходить и в мир разработки под iOS. Сейчас это уже полноценный инструмент, который можно использовать, но пока с ограничениями.
В актуальной версии SPM не поддерживает ресурсы (ждем SE-0271). У нас каждый модуль является атомарной самодостаточной зависимостью, которую можно подключить к проекту, так что ресурсы необходимы (локализации, ассеты).
А пока мы ждем, можно подготовиться к миграции: понять, насколько это сложно, можно ли это автоматизировать и с какими проблемами можно столкнуться.
Зачем мигрировать с Cocoapods на SPM?
- Нативность и интеграция в экосистему. Уже сейчас Xcode предлагает создать или добавить SPM Package.
- В отличие от Cocoapods, больше не обязательно иметь workspace для работы над проектом с зависимостями.
- Добавив новую зависимость, не нужно делать pod install и пересобирать весь проект.
- Удобно использовать локальные зависимости: не меняется структура папок, быстрее пересобирается.
- SPM не меняет структуру файла проекта, как это делает Cocoapods, не требует зависимости от Ruby и даже может сгенерировать файл проекта сам (пока недоступно для iOS-проектов).
- Cocoapods не успевает справляться с нововведениями в Xcode и компилятор Swift.
- Не нужно создавать проект Example для каждого репозитория: Xcode умеет открывать Package.swift файл как проект.
- Не нужно оптимизировать дерево зависимостей при помощи abstract_target: SPM из коробки умеет не перекомпилировать код для разных таргетов с общими зависимостями.
- Можно смотреть blame в зависимостях, а не только в основном проекте.
- Не нужно писать скрипты на Ruby, чтобы починить конфигурацию проекта.
Как выглядит миграция?
Если ваш проект имеет достаточно много зависимостей, то процесс миграции не сделать за один день. Количество зависимостей в Podfile не равно общему количеству зависимостей, так как одна зависимость может идти в комплекте с еще десятью. Даже если у вас не много зависимостей, но вы разрабатываете свои зависимости, возможно, даже в приватных репозиториях, быстро не мигрировать. К счастью, SPM отлично уживается с Cocoapods, и процесс миграции можно вести итерационно. Главное — учесть нюансы:
- Не должно быть пересечений между зависимостями SPM и Cocoapods.
- Миграция должна быть встроена в процесс развития компонентов. Если компонент развивается, поднятие версии должно выполняться как для Cocoapods, так и для SPM.
- Сложностей для команды быть не должно, а значит, нужно внедрить установку всех зависимостей через единую cli-команду.
- Миграция должна быть встроена в CI CD. Проект должен собираться как локально так и на агентах CI, во всех конфигурациях.
С чего начать?
Прежде всего собираем полный список ваших зависимостей — он представлен в Podfile.lock. Для этого можно использовать утилиты, например SPMReady.
Далее группируем их по связям. Например, от SMENetwork у нас зависят почти все компоненты, а значит, мы не сможем мигрировать их на SPM, пока не мигрирует сам SMENetwork.
Затем выделяем один модуль, от которого зависит больше всего модулей, — SMENetwork.
И наконец переходим в репозиторий этого модуля, чекаутим его в папку и создаем в руте этой папки модуль.
git clone ssh://.../smenetwork.git Developer/SMENetwork
cd Developer/SMENetwork
swift package init
Creating library package: SMENetwork
Creating Package.swift
Creating Sources/
Creating Sources/SMENetwork/SMENetwork.swift
Creating Tests/
Creating Tests/LinuxMain.swift
Creating Tests/SMENetworkTests/
Creating Tests/SMENetworkTests/SMENetworkTests.swift
Creating Tests/SMENetworkTests/XCTestManifests.swift
В результате мы получаем основной файл Package.swift и папки Sources и Tests. Если они у вас уже были, их не перетрет. Файлы SMENetwork.swift, SMENetworkTests.swift, XCTestManifests.swift, LinuxMain.swift созданы для примера работы Package, их можно удалить.
Файл Package.swift выглядит следующим образом:
Теперь посмотрим на файл SMENetwork.podspec:
Как видно, Sources и Tests лежат в других папках. Перенесем файлы и проверим работоспособность Cocoapods Example проекта.
Добавляем в Package.swift платформу разработки и переносим зависимости из podspec файла сюда. Убеждаемся, что они поддерживают SPM: для этого достаточно проверить, что в Гитхабе есть файл Package.swift. Если его нет и даже нет PR с ним, сделаем PR и поможем Open-Source community.
Для SPM не нужно создавать Example проект: достаточно открыть Package.swift в Xcode и он сам скачает и подключит зависимости, создаст таргеты согласно нашей спецификации. Нам остается только запустить.
В нашем случае почти все заработало из коробки, за исключением одного места, где забыли import Foundation: iOS-разработчики привыкли, что UIKit и Foundation необязательны, ведь работает и без них. Пора отучаться от этой привычки.
Package компилируется, тесты компилируются, но не проходят.
И тут мы упираемся в ограничение отсутствия ресурсов в Package, тесты основаны на моках, которые лежат в json ждем SE-0271…
Итог
Переехать с Cocoapods на SPM несложно. Шаги понятны, но flow монотонен и требует автоматизации.
Без особых усилий исходя из .podspec файла можно создать Package и закоммитить его в репозиторий. А самое главное — можно переехать одним проектом, а другой останется на Cocoapods. Если захочет, конечно.