(ч.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 стал ещё удобнее. Научимся спокойно переключаться на другие важные задачи (пить кофе и смотреть котиков), а не постоянно проверять терминал в надежде, что все уже установлено.


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