Жизненный цикл и система
Состояния приложения и последовательность запуска, фоновое выполнение и push, диплинки и App Group'ы, разрешения, локализация и доступность.
11 вопросов
JuniorТеорияОчень частоКаковы пять состояний приложения и какой callback срабатывает на каждом ключевом переходе?
Каковы пять состояний приложения и какой callback срабатывает на каждом ключевом переходе?
Приложение в одном из пяти состояний — not running, inactive, active, background, suspended. Запуск доходит до inactive, didBecomeActive делает его active, прерывание возвращает в inactive, didEnterBackground уводит в background, затем система тихо переводит в suspended.
Типичные ошибки
- ✗Думают, что suspended-приложение всё ещё может выполнять код в фоне
- ✗Ждут callback делегата, когда система переводит в suspended или убивает приложение
- ✗Путают краткое состояние inactive с состоянием background
Уточняющие вопросы
- →Что ненадолго переводит активное приложение в состояние inactive?
- →Почему на переходе в suspended не срабатывает ни один callback делегата?
JuniorТеорияОчень частоЧто происходит при повторном запросе разрешения (камера, геолокация) и к чему приводит отсутствие NSUsageDescription?
Что происходит при повторном запросе разрешения (камера, геолокация) и к чему приводит отсутствие NSUsageDescription?
Системный alert показывается лишь один раз — первый запрос его показывает, а второй возвращает прежний выбор без показа, поэтому изменить отказ можно только в Settings. Отсутствие строки NSUsageDescription фатально — ОС завершает приложение в момент обращения к этому API.
Типичные ошибки
- ✗Думают, что повторный запрос снова спрашивает, а не возвращает сохранённый выбор
- ✗Считают, что без строки-назначения API просто вернёт
nil, а не упадёт - ✗Полагают, что отказ можно заново разрешить прямо в приложении
Уточняющие вопросы
- →Как отправить пользователя на страницу приложения в Settings?
- →Почему iOS падает, а не мягко отказывает при отсутствии строки-назначения?
JuniorТеорияЧастоЧто может выполнить каждый из beginBackgroundTask, BGAppRefreshTask, BGProcessingTask и фоновый URLSession и как долго?
Что может выполнить каждый из beginBackgroundTask, BGAppRefreshTask, BGProcessingTask и фоновый URLSession и как долго?
beginBackgroundTask даёт короткое конечное окно при уходе в фон. BGAppRefreshTask ненадолго будит для обновления контента, а BGProcessingTask тянет долгое обслуживание при зарядке. Фоновый URLSession ведёт передачи в системном демоне, переживающем suspension.
Типичные ошибки
- ✗Считают, что любой из них позволяет бесконечно выполнять произвольный код
- ✗Думают, что фоновый
URLSessionтребует держать приложение живым в памяти - ✗Ждут, что
BGAppRefreshTaskсработает по точному выбранному расписанию
Уточняющие вопросы
- →Что будет, если не вызвать
endBackgroundTaskдо истечения окна? - →Почему момент запуска
BGAppRefreshTaskрешает система, а не ваш код?
JuniorДизайнЧастоНужно уметь открывать приложение по ссылке. Сравните собственную URL-схему (myapp://…) с Universal Link — https://-ссылкой на вашем домене. Объясните, что делает файл apple-app-site-association (AASA), как с ним связан entitlement Associated Domains и что происходит, когда приложение не установлено. Почему Universal Link считается безопаснее собственной схемы?
Нужно уметь открывать приложение по ссылке. Сравните собственную URL-схему (myapp://…) с Universal Link — https://-ссылкой на вашем домене. Объясните, что делает файл apple-app-site-association (AASA), как с ним связан entitlement Associated Domains и что происходит, когда приложение не установлено. Почему Universal Link считается безопаснее собственной схемы?
Собственную схему myapp:// может зарегистрировать любое приложение, и она не срабатывает без установленного приложения. Universal Link — настоящий https://-URL: iOS проверяет владение доменом через файл apple-app-site-association (AASA) и entitlement Associated Domains, затем открывает приложение либо ведёт на сайт.
Типичные ошибки
- ✗Утверждают, что собственная схема проверяется и защищена от перехвата
- ✗Думают, что Universal Link не требует серверного файла или entitlement
- ✗Считают, что Universal Link показывает ошибку вместо перехода на сайт
Уточняющие вопросы
- →Где должен лежать файл AASA и почему он должен отдаваться по HTTPS?
- →Как ведёт себя iOS, когда два приложения регистрируют одну схему?
JuniorТеорияЧастоЧто происходит между тапом пользователя и didFinishLaunching и зачем нужен SceneDelegate?
Что происходит между тапом пользователя и didFinishLaunching и зачем нужен SceneDelegate?
Система запускает процесс, dyld грузит и линкует динамические библиотеки бинарника, затем создаётся app delegate и вызывается didFinishLaunching. SceneDelegate ведёт UI-жизненный цикл окна, а app delegate обрабатывает события процесса.
Типичные ошибки
- ✗Считают, что до
didFinishLaunchingне происходит загрузки процесса и библиотек - ✗Думают, что
SceneDelegateобрабатывает события уровня процесса, а не UI окна - ✗Путают порядок, в котором работают app delegate и scene delegate
Уточняющие вопросы
- →Почему множество динамических фреймворков замедляет запуск до
main? - →Как scene delegate позволяет открыть больше одного окна на iPad?
MiddleДизайнЧастоПриложению нужен remote push. Пройдите весь путь — как устройство получает token, роли сервиса Apple Push Notification service (APNs) и вашего сервера и как payload доходит до пользователя. Затем сравните обычный видимый alert, тихий фоновый push (content-available) и rich-уведомление с картинкой. Наконец, объясните, что реально доставляется, когда пользователь принудительно закрыл приложение, и почему состояние жизненного цикла меняет то, на что можно полагаться. Как спроектировать регистрацию, доставку и обработку, чтобы фича была надёжной для всех трёх видов push и во всех состояниях запуска?
Приложению нужен remote push. Пройдите весь путь — как устройство получает token, роли сервиса Apple Push Notification service (APNs) и вашего сервера и как payload доходит до пользователя. Затем сравните обычный видимый alert, тихий фоновый push (content-available) и rich-уведомление с картинкой. Наконец, объясните, что реально доставляется, когда пользователь принудительно закрыл приложение, и почему состояние жизненного цикла меняет то, на что можно полагаться. Как спроектировать регистрацию, доставку и обработку, чтобы фича была надёжной для всех трёх видов push и во всех состояниях запуска?
Устройство получает token от APNs и передаёт его серверу; сервер шлёт payload в APNs. Обычный push уведомляет, тихий content-available push ненадолго будит приложение для загрузки, а rich-push добавляет медиа через service extension. После принудительного закрытия приходят лишь видимые alert'ы.
Типичные ошибки
- ✗Думают, что сервер общается с устройством напрямую, а не через APNs
- ✗Считают, что тихие push приходят и после принудительного закрытия приложения
- ✗Полагают, что медиа rich-уведомления рисуется без service extension
Уточняющие вопросы
- →Как notification-service extension меняет payload до показа?
- →Почему тихий push может быть придушен или вовсе отброшен системой?
MiddleДебаггингЧастоИсправьте Universal Link, который после холодного старта открывает не тот экран
Исправьте Universal Link, который после холодного старта открывает не тот экран
При холодном старте iOS доставляет NSUserActivity в willConnectTo через connectionOptions, а не через scene(_:continue:). Код обрабатывает только тёплый путь continue, поэтому холодный запуск из Messages открывает не тот экран. Читайте её при подключении и ведите через тот же обработчик.
Типичные ошибки
- ✗Обрабатывают только тёплый путь
continueи игнорируютconnectionOptions - ✗Считают, что холодный и тёплый запуск доставляют activity через один callback
- ✗Хватаются за аргументы запуска или запрос к серверу вместо опций сцены
Уточняющие вопросы
- →Почему роутер должен переживать вызов навигации до появления UI на экране?
- →Как обработать ссылку, что приходит и как
URLContextпри подключении?
JuniorДизайнИногдаВы собрали свой нажимаемый control из обычной view. Что нужно как минимум, чтобы он работал с VoiceOver и Dynamic Type — какие accessibility label, traits, hint и работа со шрифтом?
Вы собрали свой нажимаемый control из обычной view. Что нужно как минимум, чтобы он работал с VoiceOver и Dynamic Type — какие accessibility label, traits, hint и работа со шрифтом?
Сделайте view accessibility-элементом с accessibilityLabel (что это) и accessibilityTraits вроде .button (как ведёт себя); добавьте accessibilityHint для результата и accessibilityValue при наличии состояния. Для Dynamic Type используйте масштабируемый стиль текста вроде .body, а не фиксированный размер.
Типичные ошибки
- ✗Считают, что VoiceOver читает кастомную view без label и traits
- ✗Пропускают
accessibilityTraits, и control не объявляется кнопкой - ✗Жёстко задают размер шрифта вместо масштабируемого стиля Dynamic Type
Уточняющие вопросы
- →Когда объединять подчинённые view в один accessibility-элемент, а когда нет?
- →Как проверить, что control масштабируется на самом большом размере Dynamic Type?
JuniorТеорияИногдаКак со String Catalog и String(localized:) устроены множественное число и RTL и что ломается при втрое длиннее немецком тексте?
Как со String Catalog и String(localized:) устроены множественное число и RTL и что ломается при втрое длиннее немецком тексте?
String(localized:) находит ключ в String Catalog во время выполнения. Множественное число берёт из вариаций CLDR каталога (one/few/many/other), а не из ручных if-проверок. RTL зеркалится сам при leading/trailing, а не left/right. Длинный немецкий текст обрезается лишь при жёсткой ширине.
Типичные ошибки
- ✗Пишут ручную
if-логику множественного числа вместо категорий CLDR - ✗Используют left/right constraint'ы, которые не зеркалятся для RTL-языков
- ✗Жёстко задают ширину меток, из-за чего длинные переводы обрезаются
Уточняющие вопросы
- →Как String Catalog хранит вариации множественного числа для одного ключа?
- →Почему для переведённого текста лучше семантический стиль, а не фиксированный размер?
SeniorТеорияИногдаКак отличить нехватку памяти, таймаут watchdog и истёкшую фоновую задачу в отчёте о краше?
Как отличить нехватку памяти, таймаут watchdog и истёкшую фоновую задачу в отчёте о краше?
Нехватка памяти проявляется как событие Jetsam, а не обычный краш — memory-отчёт с причиной per-process-limit и без backtrace. Убийство watchdog за пропуск дедлайна жизненного цикла несёт код 0x8badf00d. Истёкшая фоновая задача завершается с 0xdead10cc. Причина завершения и код и различают эти три случая.
Типичные ошибки
- ✗Ждут, что убийство по памяти выглядит как обычный краш с backtrace
- ✗Путают код watchdog
0x8badf00dс кодом фоновой задачи0xdead10cc - ✗Считают, что истёкшая фоновая задача не оставляет следа в отчёте
Уточняющие вопросы
- →Почему Jetsam-отчёт показывает footprint памяти, а не упавший поток?
- →Какой ресурс обычно защищает завершение
0xdead10cc?