WWDC25: Optimize SwiftUI performance with Instruments | Apple

WWDC25: Optimize SwiftUI performance with Instruments | Apple

Как улучшить производительность SwiftUI приложений?

Введение в проблемы производительности

  • Приветствие от Джеда и Стивена, обсуждение важности анализа кода для выявления узких мест.
  • Симптомы проблем с производительностью: задержки, зависания, проблемы с анимацией и прокруткой.
  • Основное внимание будет уделено диагностике проблем производительности в коде SwiftUI.

Инструменты для диагностики

  • Использование нового инструмента SwiftUI в Instruments 26 для выявления ненужных обновлений.
  • Пример приложения "Landmarks", которое показывает расстояние до достопримечательностей и проблемы с плавностью прокрутки.

Новый инструмент SwiftUI

  • Обзор нового инструмента SwiftUI, который помогает идентифицировать проблемы с производительностью.
  • Включает Time Profiler для анализа работы CPU и инструменты Hangs and Hitches для отслеживания отзывчивости приложения.

Анализ данных профилирования

  • Первоначальный анализ информации из инструмента SwiftUI: "Update Groups" показывает работу SwiftUI.
  • Три ключевых типа обновлений: Long View Body Updates, Long Representable Updates и Other Long Updates. Эти категории помогают определить долгие обновления.

Начало профилирования приложения

  • Установка Xcode 26 и обновление ОС на устройстве для записи трассировок SwiftUI.
  • Процесс профилирования приложения "Landmarks": запуск Instruments через Xcode и выбор шаблона SwiftUI.

Исследование результатов профилирования

  • Остановка записи после прокрутки списка достопримечательностей; данные обрабатываются для анализа.
  • Проверка верхнего уровня длинных обновлений в инструменте SwiftUI; акцент на длительных обновлениях тела представления как основной причине проблем с производительностью.

Подробный анализ подкатегорий обновлений

  • Расширение трека SwiftUI для просмотра подкатегорий: View Body Updates, Representable Updates и Other Updates.
  • Иерархический обзор всех модулей обновлений тела представления во время сеанса профилирования.

Как оптимизировать обновления представлений в SwiftUI?

Фильтрация и анализ обновлений

  • Для фильтрации длинных обновлений можно использовать выпадающее меню, выбрав "Long View Body Updates". Это позволяет увидеть количество длинных обновлений для дальнейшего анализа.
  • Выбор опции "Show Updates" открывает последовательный список всех длинных обновлений для данного представления. Установка диапазона инспекции помогает сосредоточиться на конкретном интервале времени.

Профилирование производительности

  • Time Profiler собирает данные о работе CPU, фиксируя текущие выполняемые функции и сохраняя эту информацию для профилирования. В панели деталей профиля отображаются стеки вызовов зафиксированных образцов.
  • При расширении основного стека вызовов видно, что большая часть времени тратится на вычисляемое свойство distance, которое вызывает два разных форматтера.

Оптимизация работы с форматтерами

  • Свойство distance преобразует расстояние до объекта в отформатированную строку. Создание форматтеров занимает много времени, что влияет на производительность приложения.
  • Каждое выполнение тела представления требует ожидания завершения форматирования текста расстояния, что может замедлить обновление интерфейса пользователя.

Влияние времени выполнения на производительность

  • Важно понимать, как работает цикл рендеринга: приложение обрабатывает события и затем обновляет интерфейс до истечения временного лимита кадра.
  • Если одно из обновлений UI занимает слишком много времени, это может привести к задержкам (hitches), когда предыдущий кадр остается видимым дольше необходимого.

Решение проблемы с задержками

  • Задержки делают анимацию менее плавной. Рекомендуется заранее рассчитывать строки расстояний и кэшировать их вместо выполнения этого во время работы тела представления.
  • Перемещение вычисления строк расстояний в класс управления местоположением позволяет избежать затрат на создание новых форматтеров каждый раз при выполнении тела представления.

Кэширование данных для повышения эффективности

  • Создание строк формата заранее и их кэширование обеспечит доступность уже рассчитанных значений при необходимости отображения в представлении.
  • Инициализация форматтеров в классе LocationFinder позволит повторно использовать их без дополнительных затрат на создание новых экземпляров.

Управление обновлениями представлений в SwiftUI

Оптимизация обновлений расстояний

  • В коде используется массив для хранения ориентиров и словарь для кэширования строк расстояний. Функция updateDistances пересчитывает строки при изменении местоположения.
  • При обновлении местоположения необходимо также обновить кэш строк. Это делается в функции didUpdateLocations, вызываемой CoreLocation.

Улучшение производительности приложения

  • После внесенных изменений, долгие обновления представлений исчезли, что улучшило производительность приложения.
  • Долгие обновления происходили только в начале трассировки, когда приложение готовилось отобразить первый кадр, что не вызывает задержек при прокрутке.

Проблемы с избыточными обновлениями

  • Избыточные обновления представлений могут также негативно сказаться на производительности приложения.
  • Много быстрых обновлений может привести к пропуску дедлайна на отправку кадра, что вызывает задержки.

Новая функция "Избранное"

  • Добавлена кнопка "сердечко" для добавления и удаления объектов из избранного. Код реализует эту функциональность через метод toggleFavorite.
  • Модель хранит массив избранных объектов и добавляет или удаляет их по мере необходимости.

Анализ производительности новой функции

  • Для проверки работы новой функции используется инструмент Instruments. Записываются действия пользователя при выборе объектов из списка.
  • Ожидается, что нажатия кнопок "избранное" вызовут соответствующие изменения в представлении без лишних обновлений.

Отладка и понимание SwiftUI

  • Обновления представлений LandmarkListItemView происходят чаще, чем ожидалось. Это требует дополнительной отладки.
  • В отличие от UIKit, где можно использовать точки останова для анализа стека вызовов, SwiftUI сложнее анализировать из-за рекурсивных обновлений внутри AttributeGraph.

Как обновляются представления в SwiftUI?

Основы работы SwiftUI

  • SwiftUI использует декларативный подход, что затрудняет понимание причин обновления представлений. Необходимо разобраться в механизме работы SwiftUI.
  • Представления соответствуют протоколу View и реализуют свойство body для определения внешнего вида и поведения, возвращая другое значение View.

Модель данных и атрибуты

  • При добавлении представления в иерархию, SwiftUI получает объект атрибута от родительского представления, который хранит структуру представления.
  • Структуры представлений часто пересоздаются, но атрибуты сохраняют свою идентичность и состояние на протяжении всего времени жизни представления.

Обновление состояния

  • Когда происходит изменение переменной состояния, SwiftUI создает новую транзакцию для обновления иерархии представлений перед следующим кадром.
  • Транзакция помечает атрибут переменной состояния как устаревший. Затем SwiftUI проходит по цепочке зависимостей, помечая каждую из них как устаревшую.

Процесс обновления

  • После завершения всех транзакций, SwiftUI определяет, что нужно нарисовать на экране. Он не может получить эту информацию из-за устаревших данных.
  • Атрибуты обновляются до тех пор, пока все необходимые данные для отображения не будут актуализированы.

Граф причин и следствий

  • Вопрос "почему запустилось мое тело представления?" сводится к вопросу "что отметило мое тело как устаревшее?" Это важно для контроля зависимостей между представлениями.
  • Новый инструмент SwiftUI предоставляет график причин и следствий (Cause & Effect Graph), который показывает отношения между изменениями состояний и обновлением представлений.

Пример с приложением Landmarks

  • Граф показывает цепочку причинно-следственных связей: изменения состояний из-за взаимодействия пользователя (например, нажатие кнопки).
  • Узел графа LandmarkListItemView.body демонстрирует множество обновлений тела из-за изменений массива избранных объектов.

Проблемы с обновлением представлений в SwiftUI

Анализ проблемы с обновлением

  • При нажатии на кнопку "Избранное" происходит обновление всех элементов на экране, а не только того, который был выбран. Необходимо разобраться в коде.
  • Используется макрос @Observable для отслеживания изменений свойств в SwiftUI. Это создает зависимость между каждым элементом и массивом избранных объектов.
  • Каждый элемент списка вызывает функцию isFavorite для определения, должен ли значок быть выделен. Это приводит к повторному выполнению тела каждого представления при изменении массива избранных.

Оптимизация зависимостей

  • При добавлении нового избранного через кнопку toggleFavorite все элементы помечаются как устаревшие, что приводит к ненужным обновлениям.
  • Для улучшения ситуации предлагается создать Observable view model для каждого представления, чтобы отслеживать статус избранного индивидуально.

Внедрение новых моделей

  • Каждое представление будет иметь свою собственную модель, что позволит избежать зависимости от полного массива избранных.
  • Теперь каждое представление может управлять своим состоянием без влияния на другие элементы интерфейса.

Результаты изменений

  • После внедрения новых моделей количество обновлений уменьшилось до двух при изменении статуса двух избранных объектов.
  • График причин и следствий показывает только два обновления вместо множества ненужных вызовов.

Работа с окружением в SwiftUI

Понимание окружения

  • Значения окружения хранятся в структуре EnvironmentValues и создают зависимость между представлениями и окружающей средой приложения.
  • При изменении любого значения окружения уведомляются все связанные представления о необходимости повторного выполнения их тела.

Обновления графа причин и следствий

  • Существуют два типа узлов: внешние обновления окружения (например, изменение цветовой схемы системы).
  • Если цветовая схема меняется из-за перехода устройства в темный режим, это отражается в графе как причина для запуска тела соответствующих представлений.

Оптимизация производительности SwiftUI

Влияние обновлений представлений на производительность

  • Обновления представлений могут происходить одновременно, что облегчает их идентификацию. Даже если тело представления не требует выполнения из-за обновления окружения, все равно существует стоимость проверки обновлений значений, что может быстро накапливаться при большом количестве представлений.
  • Избегайте хранения часто обновляющихся значений (например, геометрических значений или таймеров) в окружении. Граф причин и следствий помогает визуализировать поток данных в приложении и гарантирует, что ваши представления обновляются только по необходимости.

Лучшие практики для повышения производительности

  • Необходимость минимизировать ненужные обновления тела представлений. Проектируйте поток данных так, чтобы ваши представления обновлялись только тогда, когда это действительно необходимо. Будьте особенно осторожны с зависимостями, которые изменяются очень часто.
  • Используйте инструменты анализа (Instruments) на ранних этапах разработки для оценки производительности приложения. Основной вывод: обеспечьте быстрое обновление тел ваших представлений только по мере необходимости для достижения высокой производительности SwiftUI.

Дополнительные ресурсы

  • Ознакомьтесь с документацией, упомянутой в описании видео, чтобы узнать о других функциях инструмента профилирования вашего приложения.
Video description

Discover the new SwiftUI instrument. We’ll cover how SwiftUI updates views, how changes in your app’s data affect those updates, and how the new instrument helps you visualize those causes and effects. To get the most out of this session, we recommend being familiar with writing apps in SwiftUI. Explore related documentation, sample code, and more: Performance and metrics: https://developer.apple.com/documentation/Xcode/performance-and-metrics Measuring your app’s power use with Power Profiler: https://developer.apple.com/documentation/Xcode/measuring-your-app-s-power-use-with-power-profiler Understanding and improving SwiftUI performance: https://developer.apple.com/documentation/Xcode/understanding-and-improving-swiftui-performance Analyzing the performance of your visionOS app: https://developer.apple.com/documentation/visionOS/analyzing-the-performance-of-your-visionOS-app Improving app responsiveness: https://developer.apple.com/documentation/Xcode/improving-app-responsiveness Optimize CPU performance with Instruments: https://developer.apple.com/videos/play/wwdc2025/308 Analyze hangs with Instruments: https://developer.apple.com/videos/play/wwdc2023/10248 Explore UI animation hitches and the render loop: https://developer.apple.com/videos/play/tech-talks/10855 Demystify SwiftUI performance: https://developer.apple.com/videos/play/wwdc2023/10160 Explore SwiftUI animation: https://developer.apple.com/videos/play/wwdc2023/10156 Compose custom layouts with SwiftUI: https://developer.apple.com/videos/play/wwdc2022/10056 Demystify SwiftUI: https://developer.apple.com/videos/play/wwdc2021/10022 00:00 - Introduction & Agenda 02:19 - Discover the SwiftUI instrument 04:20 - Diagnose and fix long view body updates 19:54 - Understand causes and effects of SwiftUI updates 35:01 - Next steps More Apple Developer resources: Video sessions: https://apple.co/VideoSessions Documentation: https://apple.co/DeveloperDocs Forums: https://apple.co/DeveloperForums App: https://apple.co/DeveloperApp