Apple по доброте душевной поделилась с разработчиками инструментом отладки SwiftUI. Удобный он или не очень — разберёмся вместе с Surf iOS Team.


По умолчанию Instruments SwiftUI открывает 5 полей сбора данных:
View Body: сколько view было создано за указанное время и сколько времени потребовалось для их создания.
View Properties: какие view properties изменились и вызвали изменения в интерфейсе. Речь тут о свойствах, на которые можно подписаться — State, ObservedObject, Binding.
Core Animation Commits: сколько было выполнено Core Animation Commits и какова была их длительность. В этом случае CAC — это единовременная задача UI framework’a по рендерингу нового состояния UI.
Time Profiler: сколько времени занял каждый вызов функции.
Hangs: зависания в главном потоке и их длительность. Работает от iOS 16.
С помощью Core Animation Commits можно искать самые «дорогостоящие» перерисовки UI и определять причины проблем — достаточно следить за полями View Body и View Properties.
Hangs поможет найти самые ощутимые фризы. Стоит обратить внимание не только на их длительность, но и на количество — сколько их идёт друг за другом.

С этим инструментом легко работать с новыми проектами, где ещё не очень много кода и не так много связей. Если же у нас «тяжелый» проект, такая отладка может занять много времени.
Упрощаем работу с инструментами
Для начальной отладки удобно использовать симулятор — он всегда под рукой и все окна будут на одном экране. Но для реальной проверки ВСЕГДА берите реальное устройство.
Инструменты работают с релизной конфигурацией, а значит, любые дебажные дополнения будут проигнорированы. Prints в том числе.
Инструменты записывают реакции на всё происходящее в приложении, так что стоит дать им время на отработку всех действий. Дождитесь открытия и полной загрузки приложения. После нажатия на кнопку или после взаимодействия с частью системы сделайте паузу — так на графиках можно будет отследить логические начало и конец каждого действия.
Полноценную аналитику данных можно увидеть только после остановки инструментов — в реальном времени они просто отобразят не все данные (чтобы не терять время на их обработку). Потому лучше сначала снимать данные, а анализировать их уже после.
Начинайте с простых последовательностей — например, с реакции на нажатие одной кнопки. Так получится локализовать логику происходящего и не бросаться в море данных.
Смотрим на примере шторки
Данные ниже собраны на основе кастомной шторки в приложении. Использовать системный компонент мы не могли — требовалась поддержка работы на iOS 14 и выше.
Запускаем инструменты, дожидаемся загрузки главного экрана — это первая череда активностей на слайде.
Берём паузу — помним, что мы никуда не торопимся, чтобы получить качественные данные для аналитики — после чего нажимаем кнопку смены города. Это вторая череда активности на скрине.
Последняя вспышка активности — закрытие шторки.
Завершаем сессию снятия данных, чтобы инструменты смогли их обработать и отобразить детали.

Меняем масштаб — cmd+ или жестом zoom на трекпаде — в месте открытия шторки. И тут же видим, что между стартом действия и концом отображения проходит больше секунды. Чтобы увидеть время, наводим мышку на нужный участок
Старт открытия шторки в 14.942 — ориентируемся на поле View Body.

Конец обработки открытия шторки на 15.993 — смотрим на поле View Body.

Видим, что за прошедшую секунду перерисовывается множество view. На шкале View Body видно много столбиков, каждый столбик — это отдельная view.
Поле Hangs — на этом масштабе видим блок мелких подвисаний microHang и оранжевый столбец довольно ощутимого зависания.
Чтобы понять, какие view перерисовались и какие вызвали эти перерисовки, меняем масштаб инструментов. Первой меняется состояние кнопки — и к этому нет вопросов.

Получается, что все подписанные экраны реагируют на переменную «шторки», хотя сами экраны не изменяются. И логично было бы их не только не перерисовывать, но и не пересчитывать.

Нашли неоднозначную связь подписок в коде, которую стоит изучить.
Спойлер
Подписка на bottomSheetContent была везде, где мы хотели вызвать шторку, иногда не только там. При этом там, где мы меняем переменную, подписка не нужна. Она требуется только в том месте, где должна произойти перерисовка шторки, а не view её содержащих. Правки помогли нам ускорить работу шторки в 2 – 4,9 раза — в зависимости от точки её открытия.
Наблюдаем за графиком
Посмотрим на поле Core Animation Commits — там, где оно становится красным.

Строка Core Animation Commits несёт общую информацию, что система проводит очень много перерисовок одновременно или делает это медленно. А детали происходящего ищем в полях View Properties и View Body.
Медленные перерисовки возникают, когда нужно обновить большие view. Тут важно понимать, действительно ли нужно перерисовывать всю view или можно переделать отдельную её часть.
Самые долгие функции надо смотреть в Time Profiler. Он умеет отображать в логах конкретные методы и места в коде. Но в рамках нашего проекта строка оказалась бесполезной.
Для поля View под графиками есть консоль вывода информации о том, сколько и какие view перерисовывались. Можно отсортировать эти данные по времени. Но для крупных проектов это просто огромная куча данных, которую довольно сложно разгрести. Так что проще обойтись информацией из графиков — там она более наглядна и структурирована.

Подводим итоги
Мы попробовали SwiftUI Instruments и выносим вердикт — использовать их, как минимум, интересно. А на новых проектах — ещё и довольно просто. Инструменты помогут лучше понимать SwiftUI.
А ещё SwiftUI Instruments можно использовать для проверки себя — они помогут понять, оптимально ли мы используем подписки. Ведь часто на UI достаточно перерисовать один блок, а не весь экран.
На больших проектах можно использовать инструменты для проведения рефакторинга, хотя, конечно, он может оказаться очень дорогим для внедрения и тестирования. Или для проверки добавления новой логики или фич — чтобы не сделать проект ещё более тяжёлым.
Кейсы и лучшие практики в области системной и бизнес-аналитики, новости, вакансии и стажировки Surf — в телеграм-канале Surf Tech.