Live Activity был показан Apple на презентации iOS 16 и нового iPhone с Dynamic Island. Обновление доступно только для тех, кто обновил iOS до 16.1.  Меня зовут Азиз, я iOS-разработчик в inDrive. В статье расскажу, как мы добавляли Live Activity в наше iOS-приложение. Постараюсь ответить на все вопросы, которые возникли у нас в процессе разработки.  Основная идея в том, что вы как пользователь можете не открывать приложение каждый раз, когда вам надо проверить важную информацию, актуальную в конкретный момент времени.  Простой пример работы Live Activity. Допустим, вы заказали некую услугу. Помимо общего статуса «К вам едет заказ», вам на заблокированном экране показывается виджет со всей необходимой информацией: статус заказа, время доставки, информация о курьере/водителе и так далее. Есть много статей, описывающих процесс добавления Live Activity в проект. За единственным исключением — ни одна статья не делится опытом добавления фичи в настоящий проект. Об этом и хочу рассказать в статье.  Наш процесс начался с демонстрации Live Activity бизнесу. Нужно было «продать» эту идею. Мы создали демо-приложение по мотивам статей и официальной документации. Получилось достаточно наглядно продемонстрировать основную идею.  После недолгих согласований мы начали внедрять Live Activity в проект inDrive. Организовали инициативную группу, в которой, помимо меня, было еще два iOS-разработчика.  При интеграции в боевой проект возник ряд проблем, с которыми надо было что-то делать:  XcodeGen и первый запуск — как добавить в project.yml новый target, да еще и с поддержкой Live Activity.  Полноценное понимание, как работают Push Notification с Live Activity.  У нас своя дизайн-система, как использовать ее в Live Activity. Как подключить переводы. Как связать UDF с Live Activity.  В нашем проекте мы используем XcodeGen для генерации *.xcodeproj файла. В этом был небольшой челендж — ранее в проекте виджеты мы не использовали. Нам пришлось добавлять определенные темплейты в project.yml основного модуля нашего приложения. В основном таргете в info надо было добавить флаг:  Затем необходимо создать темплейт для самого Live Activity виджет-тагрета: И не забыть в основном таргете:  С указанием соответствующего bundleId, который предварительно надо не забыть привязать к Provision Profile (тоже  отдельный для этого бандла). После всех манипуляций и заветного: make finished successfully 🎉, надо показать Live Activity первый раз. Мы опустим момент с настройкой Content State и определения статичных свойств и тех, что должны обновляться. Важно не забыть добавить main —  без него виджет не сможет запуститься.   Покажем сразу, как именно запустить Live Activity (iOS 16.1):  Мы должны убедиться, что у пользователя включена в настройках возможность отображать Live Activty и нет запущенных активити.  При создании запроса на Activity нам надо передать pushType: .token — хотите обновлять Live Activity посредством Push Notifications. nil — только в рамках жизненного цикла приложения.  Если через токен, то асинхронно придет pushToken, который надо отправить на бэк. Именно так бэк поймет, что мы готовы получать обновления для Live Activity. Токен для Live Activity имеет свойство обновляться в зависимости от того, осталось приложение запущенным или было выгружено. Надо отслеживать этот момент и сообщать об этом на бэкенд. Чтобы обновлять Activity из самого приложения, если не хотите использовать Push Notification (iOS 16.1):  И завершать:  После вызова метода startWith() вы должны увидеть Live Activity. Если пройдете этот путь, делитесь кейсами в комментариях.  Изначально мы реализовали основной функционал без пушей. Live Activity устроен так, что его можно реализовать, не используя Push Notifications — просто на базе статусов внутри приложения. Затем задались вопросами:  Как Live Activity понимает, что push именно для нее? На самом деле это все подкапотная магия Apple. Система сама определяет по пришедшему Payload пуша и id, к какому Live Activity относится пришедшая информация. Можно ли как-то ловить эти пуши в самом приложении? На момент написания статьи Apple не предоставляло никакой информации о том, как отловить пуш в запущенном приложении. Мы с ребятами проверяли, срабатывает ли метод:  В случае с Live Activity Push Notification этот метод не вызовется. Как тестировать пуши, пока нет бэкенда? Мы сами придумали подходящий Payload и реализовали на тестовом приложении обычный флоу пуш-уведомлений. Дальше использовали это.  Также поясню, что большая часть работы предстояла на бэкенде. Мы передали примеры запросов в APNS c указанием, какой Payload будет ждать клиент. Более подробно про реализацию бэкенда расскажем во второй части статьи.    Ранее в проекте мы не использовали SwiftUI, поэтому это для нас стало еще одним вызовом. После успешного запуска в демо-приложении, мы начали наводить красоту и пробовать использовать общие компоненты из дизайн-системы. Базовые компоненты: цвета, шрифты, иконки не составили больших проблем, так как начиная с версии iOS 15, Apple добавили простую возможность использовать компоненты UIKit в SwiftUI. Мы пошли немного дальше и реализовали это нативно. Кстати, базовые компоненты мы хотим выложить в open source — можно будет посмотреть.  Apple предоставляет простой механизм для обертки UIView в SwiftUI.View — UIViewRepresentable, который должен был облегчить нам жизнь, но получилось как-то так:   Или так:  Хотя планировалось так:) И так: В интернете мы не нашли этому объяснения и решили отказаться от части фич, пока не переведем компоненты на нативные SwiftUI. Если у вас есть идеи, почему так происходит, будем рады почитать. В основном приложении мы не меняем цвет автомобиля, который едет к пользователю. Это иконка зашита в приложении — мы просто ее подставляем. Для LiveActivity я взял иконку из Figma, поставил и забыл.  Потом пришел мой коллега и сказал: «А почему нам не красить машинки в тот цвет, который приходит с бэкенда?». Мы выяснили, что цвет приходит просто в виде текста: Gold, Blue и так далее. Попросили бэк отправлять еще hex, чтобы можно еще и красить.  Тут пришлось немного схитрить. Мы разбили изображение на несколько слоев: кузов, который будем красить, и базу автомобиля, которая будет неизменной. Получилось как-то так:  Таким простым способом добавили информативности для виджета. Мы работаем в 47 странах мира и поддерживаем множество языков. Запуская фичу, мы должны поддерживать все языки, включая арабский (где правописание справа налево).  Для удобной работы с переводами мы используем платформу Crowdin, а для виджета и острова необходимо настроить положение в зависимости от локализации. Мы использовали для этого радости, которые дает нам SwiftUI в виде Environment. Благодаря UDF мы можем реализовать service component, который будет «слушать» события изменения определенного State, формировать наш Live Activity и обновлять его при необходимости.  Как я показал выше, мы можем завернуть старт, обновление и завершение Live Activity через service component. Это будет полезно в том случае, если вы захотите обновлять состояние через запущенное приложение (без remote push notification).  Чтобы обновлять Live Activity через пуши, вам явным образом надо запустить его в активном приложении. Дальше всю магию Apple берет на себя. Далее можно ловить LiveActivityAction в нужном редьюсере и отправлять запросы на бэкенд с токеном для получения push-уведомлений. Через них можно как обновить, так и завершить Live Activity. Это достаточно подробно описано в документации Apple. Для первой части получилось достаточно объемно, поэтому в следующем году поделюсь дополнительными наблюдениями относительно Live Activity. Отдельную благодарность хочу сказать Леше Какоулину и Пете Казакову — без этих ребят ничего бы не получилось.  Полезные ссылки: Документация Apple  Туториал по Live Activity  UDF  Статья о нашей дизайн-системе  iOS developer