(ч.2) Удобный pod install
В прошлой части мы сократили команду bundle exec pod install до pods install. Всего на одну букву больше чем обычный pod install. А в этот раз давайте посмотрим какие ещё неприятные моменты бывают с использованием этой команды.
Опишем какие сценарии использования pod install существуют. Но перед этим нужно разобраться какие файлы участвуют в анализе зависимостей при выполнении этой команды:
- Podfile описывает необходимые зависимости;
- Podfile.lock описывает установленные зависимости;
- Manifest.lock локальная копия Podfile.lock. Находится в папке Pods.
Сценарий 1: Новый проект
Новый проект без CocoaPods. Вызываем pod init, затем описываем зависимости в Podfile. И наконец вызываем pod install в первый раз. В момент вызова CocoaPods смотрит, что файла Podfile.lock ещё нет. Затем устанавливает все зависимости описанные в Podfile. В конце создается Podfile.lock и его локальная копия Manifest.lock.
Сценарий 2: Мы что-то изменили
В этом сценарии мы меняем Podfile или *.podspec у локальной зависимости. Возможно мы добавили новую зависимость или изменили описание. При вызове происходит обновление Podfile.lock и его локальной копии Manifest.lock.
На мой взгляд, в первых двух сценариях оптимизировать нечего. Но не хотелось бы сломать их нашей будущей оптимизацией.
Сценарий 3: Синхронизация
Загружаем новый проект или новые изменения из git, где уже существует Podfile.lock. Тогда при вызове команды обычно происходит сравнение зависимостей из двух файлов: Podfile.lock и вашего локального файла Manifest.lock. Происходит обновление зависимостей из Podfile.lock, и копирование его в Manifest.lock.
Все как обычно, но есть один нюанс. Проект будет генерироваться заново, даже если Podfile.lock и Manifest.lock одинаковые. Другими словами, если pod install не был нужен, а вы его вызвали, то он не отработает мгновенно и не скажет, что он вовсе не был нужен сейчас.
На моем рабочем проекте такой запуск в холостую занимает целую минуту. Может это не так уж и много, но явно это время можно использовать с большей пользой.
Решение проблемы в Xcode
На самом деле решение этой проблемы уже есть в Build Phases проекта, сгенерированного с помощью CocoaPods. То есть в этом сценарии всегда нужно сначала попробовать собрать проект, а затем уже вызывать pod install в терминале.
В этом скрипте происходит сравнение Podfile.lock и Manifest.lock с помощью команды diff. И если они не равны, то в Xcode будет знакомая ошибка The sandbox is not in sync ...
В целом это отличное решение, но что если вы все же забыли? Открыли терминал и вызвали сначала ненужный pod install?
Просто подожди минуту! - скажете вы. Но я не могу ждать. Тем более это не последнее, что я бы хотел сделать более удобным в pod install.
Свой pod install с блэкджеком
А почему бы собственно и нет? Хотя нет. Это долго и бессмысленно. А ещё нужно поддерживать при выходе новых версий, которые могут все сломать.
Но почему бы просто не написать небольшую обертку над CocoaPods и перед вызовом pod install добавить проверку, как это сделано в Xcode?
Все что мы сделаем нужно поместить в ваш .bash_profile, как мы это делали в прошлой части. Сначала создадим простую функцию обертку pods_install. С помощью этой функции мы сможем добавить проверку перед вызовом pod install. И ещё много всего.
function pods_install() { # TODO: Add compare Podfile.lock and Manifest.lock bundle exec pod install "$@"}
Далее нам нужно сделать так, чтобы все работало точно так же, как и раньше. И ничего не сломать. Вместо alias из прошлой статьи мы создадим функцию pods. Если в эту функцию будет передан аргумент install, то мы выбросим этот аргумент и вызовем pods_install. Иначе просто заменим pods на bundle exec pod.
# Instead of the alias from the last postfunction pods() { case $* in install ) shift 1; pods_install "$@" ;; * ) command bundle exec pod "$@" ;; esac}
Пока все работает, как и раньше. Осталось добавить проверку в pods_install. Я скопировал код из Xcode и максимально сократил его для простоты. Используется команда diff для сравнения. Лог команды не выводится в терминал. После выполнения проверяем значение результата команды. Если оно равно нулю, значит файлы одинаковые. Выходим из функции, пропускаем pod install.
function pods_install() { diff "Podfile.lock" "Pods/Manifest.lock" > /dev/null if [ $? == 0 ] ; then return fi bundle exec pod install "$@"}
Кажется всё готово? Давайте проверим все сценарии.
- Новый проект. При вызове получаем diff: Podfile.lock: No such file or directory. Затем выполняется pod install и создается Podfile.lock. В целом ОК;
- Мы что-то изменили в Podfile или *.podspec у локальной зависимости. При вызове ничего не происходит, так как Podfile.lock и Manifest.lock остались прежними. Это плохо, надо придумать как можно принудительно запускать pod install. Всё не ОК;
- Синхронизация. Если в git не было изменений в Podfile.lock, то pod install не вызывается. Если были, то локальный Manifest.lock отличается от него и pod install вызывается. Всё ОК.
Force pod install
Самый простой способ исправить второй сценарий, это добавить новый режим. В этом режиме мы всегда будем вызывать pod install, без проверки Podfile.lock и Manifest.lock.
Давайте сначала изменим функцию pods_install. Для простоты будем проверять только первый аргумент. Если он равен -f, тогда пропускаем шаг, где сравниваем Podfile.lock и Manifest.lock. Иначе пропускаем первый аргумент и передает все остальное в pod install.
function pods_install() { # Check the first argument and if it is -f if [ "$1" == "-f" ] ; then # Throw it away so as not to pass it to pod install shift 1; else # Otherwise all as before diff "Podfile.lock" "Pods/Manifest.lock" > /dev/null if [ $? == 0 ] ; then return fi fi bundle exec pod install "$@"}
Осталось немного обновить функцию pods. Опять же для простоты я решил использовать команду pods install!. Это чуть короче в использовании и проще в реализации. В функции проверим если аргумент равен install!, то при вызове pods_install передадим первым аргументом -f, а затем остальные аргументы. В остальном без изменений.
function pods() { case $* in install! ) shift 1; pods_install -f "$@" ;; install ) shift 1; pods_install "$@" ;; * ) command bundle exec pod "$@" ;; esac}
Добавляем сообщение
Добавим небольшое сообщение о том, что в этот раз изменений не было и pod install не вызывался. Финальная версия будет выглядеть так.
function pods_install() { if [ "$1" == "-f" ] ; then shift 1; else diff "Podfile.lock" "Pods/Manifest.lock" > /dev/null if [ $? == 0 ] ; then echo " Everything is up to date." return fi fi bundle exec pod install "$@"}function pods() { case $* in install! ) shift 1; pods_install -f "$@" ;; install ) shift 1; pods_install "$@" ;; * ) command bundle exec pod "$@" ;; esac}
Результат
- В этой части мы сделали pod install ещё немного удобнее;
- Подготовили Bash среду для новых модификаций;
- И ничего не сломали (но это не точно).
••• pods install Everything is up to date.
••• pods install!Analyzing dependenciesDownloading dependenciesInstalling Alamofire (5.2.0)Pod installation complete!There are 2 dependencies from the Podfile and 2 total pods installed.
В следующей части мы постараемся сделать так, чтобы pod install стал ещё удобнее. Научимся спокойно переключаться на другие важные задачи (пить кофе и смотреть котиков), а не постоянно проверять терминал в надежде, что все уже установлено.
💬 Вы можете оставить свой фидбек в этом твите:
(ч.2) Удобный pod install 🌱#ios #tools #cocoapods #bashhttps://t.co/3Rf0JiZYLC pic.twitter.com/83Tzepxk1X
— Ну Слаааава (@swiftyfinch) May 31, 2020