(ч.2) Удобный pod install herb.svg

31.05.2020
5 мин

В прошлой части мы сократили команду bundle exec pod install до pods install. Всего на одну букву больше чем обычный pod install. А в этот раз давайте посмотрим какие ещё неприятные моменты бывают с использованием этой команды.

Опишем какие сценарии использования pod install существуют. Но перед этим нужно разобраться какие файлы участвуют в анализе зависимостей при выполнении этой команды:

  • Podfile описывает необходимые зависимости;
  • Podfile.lock описывает установленные зависимости;
  • Manifest.lock локальная копия Podfile.lock. Находится в папке Pods.

rocket.gif Сценарий 1: Новый проект

Новый проект без CocoaPods. Вызываем pod init, затем описываем зависимости в Podfile. И наконец вызываем pod install в первый раз. В момент вызова CocoaPods смотрит, что файла Podfile.lock ещё нет. Затем устанавливает все зависимости описанные в Podfile. В конце создается Podfile.lock и его локальная копия Manifest.lock.

edit.svg Сценарий 2: Мы что-то изменили

В этом сценарии мы меняем Podfile или *.podspec у локальной зависимости. Возможно мы добавили новую зависимость или изменили описание. При вызове происходит обновление Podfile.lock и его локальной копии Manifest.lock.

На мой взгляд, в первых двух сценариях оптимизировать нечего. Но не хотелось бы сломать их нашей будущей оптимизацией.

sync.svg Сценарий 3: Синхронизация

Загружаем новый проект или новые изменения из git, где уже существует Podfile.lock. Тогда при вызове команды обычно происходит сравнение зависимостей из двух файлов: Podfile.lock и вашего локального файла Manifest.lock. Происходит обновление зависимостей из Podfile.lock, и копирование его в Manifest.lock.

Все как обычно, но есть один нюанс. Проект будет генерироваться заново, даже если Podfile.lock и Manifest.lock одинаковые. Другими словами, если pod install не был нужен, а вы его вызвали, то он не отработает мгновенно и не скажет, что он вовсе не был нужен сейчас.

На моем рабочем проекте такой запуск в холостую занимает целую минуту. Может это не так уж и много, но явно это время можно использовать с большей пользой.

xcode.svg Решение проблемы в Xcode

На самом деле решение этой проблемы уже есть в Build Phases проекта, сгенерированного с помощью CocoaPods. То есть в этом сценарии всегда нужно сначала попробовать собрать проект, а затем уже вызывать pod install в терминале.

В этом скрипте происходит сравнение Podfile.lock и Manifest.lock с помощью команды diff. И если они не равны, то в Xcode будет знакомая ошибка The sandbox is not in sync ...

В целом это отличное решение, но что если вы все же забыли? Открыли терминал и вызвали сначала ненужный pod install?

Просто подожди минуту! - скажете вы. Но я не могу ждать. Тем более это не последнее, что я бы хотел сделать более удобным в pod install.

bender.svg Свой 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 post
function 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 "$@"
}

bug.svg Кажется всё готово? Давайте проверим все сценарии.

  • Новый проект. При вызове получаем 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.svg 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
}

loudspeaker.svg Добавляем сообщение

Добавим небольшое сообщение о том, что в этот раз изменений не было и pod install не вызывался. Финальная версия будет выглядеть так.

function pods_install() {
if [ "$1" == "-f" ] ; then
shift 1;
else
diff "Podfile.lock" "Pods/Manifest.lock" > /dev/null
if [ $? == 0 ] ; then
echo "party.png 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
}

party.png Результат

  • В этой части мы сделали pod install ещё немного удобнее;
  • Подготовили Bash среду для новых модификаций;
  • И ничего не сломали (но это не точно).
••• pods install
party.png Everything is up to date.
••• pods install!
Analyzing dependencies
Downloading dependencies
Installing Alamofire (5.2.0)
Pod installation complete!
There are 2 dependencies from the Podfile and 2 total pods installed.

В следующей части мы постараемся сделать так, чтобы pod install стал ещё удобнее. Научимся спокойно переключаться на другие важные задачи (пить кофе и смотреть котиков), а не постоянно проверять терминал в надежде, что все уже установлено.


💬 Вы можете оставить свой фидбек в этом твите: