Вопросы на собеседовании: Мобильный разработчик
100 реальных вопросов с образцовыми ответами и пояснениями для уровня Middle.
Смотреть пример резюме: Мобильный разработчик →Тренировка флешкарточками
Интервальное повторение · Hunter Pass
Вопросы
Я оставляю за ViewModel состояние представления и обработку действий пользователя, но не навигацию, хранение данных или платформенные UI-объекты.
- View отображает неизменяемое состояние и передаёт события вроде retryTapped или queryChanged.
- Use case содержит бизнес-решения, а репозитории скрывают сеть и базу данных.
- Router или coordinator отвечает за навигацию, поэтому ViewModel тестируется без UIKit, SwiftUI или Android Navigation.
Зачем это спрашивают: Интервьюер проверяет, умеете ли вы применять MVVM как набор границ, а не переносить всю ответственность в одну большую ViewModel.
Зависимости направлены к стабильным бизнес-правилам, поэтому доменный слой не импортирует UI, базы данных и сетевые фреймворки.
- Доменный use case зависит от интерфейса репозитория, а data-слой реализует его через Room, Core Data или HTTP-клиент.
- Модели фреймворков преобразуются на границе и не протекают в домен как ответы Retrofit или NSManagedObject.
- За это приходится платить маппингом, поэтому я разделяю слои там, где вероятна смена бизнес-логики или источников данных, а не для каждого простого значения.
Зачем это спрашивают: Сильный ответ объясняет направление зависимостей и признаёт стоимость маппинга, а не описывает слои как папки.
Репозиторий предоставляет домену единый контракт данных и координирует за ним локальный и удалённый источники.
- Чтение может наблюдать за локальной базой, пока обновление записывает в неё данные из API.
- Изменения сначала сохраняются локально с метаданными синхронизации, а затем ставятся в очередь на отправку при появлении сети.
- Репозиторий возвращает доменные результаты и явный статус актуальности или синхронизации, не раскрывая Room, Core Data и транспортные DTO.
Зачем это спрашивают: Интервьюер ожидает координацию источников и стабильный доменный контракт, а не репозиторий, который только переименовывает вызовы API.
Я добавляю use case, когда операция выражает переиспользуемое бизнес-поведение, но не создаю однострочные обёртки без логики.
- Checkout может объединять валидацию, правила наличия товара и запись через репозиторий в одной именованной операции.
- Один use case можно использовать на нескольких экранах и тестировать без UI-фреймворка.
- Если GetProfile лишь вызывает repository.getProfile без политики или композиции, дополнительный тип усложняет навигацию по коду и не создаёт реальной границы.
Зачем это спрашивают: Вопрос проверяет, отличаете ли вы осмысленную бизнес-границу от архитектуры, добавленной только ради следования паттерну.
Я моделирую единое неизменяемое состояние экрана, свожу действия пользователя к переходам состояния, а асинхронную работу изолирую в эффектах.
- View отправляет действия Load и Retry, но не меняет отдельные поля напрямую.
- Reducer явно задаёт переходы, например Error в Loading, затем в Content, поэтому противоречивые флаги не возникают.
- Эффекты вызывают репозитории и возвращают результаты как действия, а тесты reducer остаются детерминированными и без побочных эффектов.
Зачем это спрашивают: Интервьюер проверяет понимание reducer, эффектов и предотвращения невозможных состояний, а не только опыт с контейнером состояния.
Scope в Hilt привязывает единственный экземпляр к времени жизни сгенерированного компонента, поэтому я выбираю его по владельцу состояния.
- SingletonComponent живёт вместе с процессом и подходит для базы данных или настроенного HTTP-клиента.
- ActivityRetainedComponent переживает смену конфигурации, а ViewModelComponent создаёт экземпляр на одну ViewModel.
- Нельзя внедрять короткоживущую Activity в singleton, потому что он может удержать уже уничтоженную Activity.
Зачем это спрашивают: Сильный ответ связывает компоненты Hilt с жизненным циклом Android и распознаёт инверсию времени жизни как риск утечки.
Я ввожу абстракцию на изменчивой границе или в точке тестирования, а не перед каждым конкретным классом.
- Интерфейс PaymentAuthorizing изолирует сторонний SDK, чьи API и тестируемость мы не контролируем.
- Внедрение через конструктор делает обязательную зависимость видимой и позволяет тестам подставить небольшой fake с предсказуемым результатом.
- Чистые преобразования значений могут оставаться конкретными, потому что interface добавит косвенность без пользы от подмены.
Зачем это спрашивают: Интервьюер оценивает, используете ли вы абстракции выборочно и можете ли назвать конкретную пользу для тестов или изменений.
Я разделяю модули по цельным фичам и стабильным общим возможностям, оставляя между ними небольшие публичные API.
- Модуль checkout может открыть точку входа и типы результата, сохранив экраны, reducers и маппинг данных внутренними.
- Core-модули содержат действительно общие вещи вроде сети или дизайн-токенов, а не коллекцию случайных helpers.
- Feature-модули зависят от доменных контрактов, а не от реализаций друг друга, что уменьшает пересборки и случайную связанность.
Зачем это спрашивают: Интервьюер проверяет, помогают ли ваши модули владению кодом и контролю зависимостей, а не просто создают произвольные папки.
Я оставляю доменные модели независимыми от фреймворков и явно преобразую их на границах хранилища, сети и UI.
- Nullable-поле API и optional-свойство Core Data могут преобразоваться в доменный тип с проверенным значением по умолчанию или ошибкой.
- Транспортные детали вроде snake_case ключей и конвертов пагинации остаются в DTO.
- Платформенные типы отображения, такие как UIImage, Bitmap, Color и локализованные строки, не попадают в доменную модель.
Зачем это спрашивают: Вопрос проверяет, умеете ли вы сохранять стабильный язык домена при работе с несовершенными внешними схемами.
Я предоставляю узкий API возможности с платформенно-нейтральными входами, результатами и правилами жизненного цикла.
- Модуль принимает идентификаторы и value objects, а не UIViewController, Activity или типы состояния конкретного фреймворка.
- Нативные адаптеры переводят callbacks или async-результаты в соглашения Swift, Kotlin, React Native или Flutter.
- Версионированные контракты и contract tests не дают изменениям bridge незаметно сломать одну платформу, пока другая продолжает компилироваться.
Зачем это спрашивают: Интервьюер ожидает границу, которая выдерживает несколько UI-стеков и не протаскивает один фреймворк через весь модуль.
Я использую async let для небольшого фиксированного набора дочерних операций, а task group для динамической коллекции.
- async let сразу запускает дочернюю работу и позволяет дождаться типизированных результатов в текущем scope.
- withThrowingTaskGroup позволяет добавить по дочерней задаче на элемент и обрабатывать результаты по мере завершения.
- Оба подхода сохраняют structured concurrency, поэтому выход из scope дожидается дочерних задач или отменяет их, не оставляя бесхозную работу.
Зачем это спрашивают: Интервьюер проверяет понимание структурированного времени жизни задач, а не только синтаксиса async и await.
Отмена задач в Swift кооперативная: она устанавливает флаг, а участвующий код должен быстро остановиться сам.
- Task.checkCancellation выбрасывает ошибку на подходящей границе, а Task.isCancelled позволяет выполнить очистку перед возвратом.
- Приостанавливаемые API вроде Task.sleep реагируют на отмену, но CPU-циклам нужны собственные периодические проверки.
- Cancellation handler может освободить ресурсы, но очистка должна быть безопасной, даже если операция уже частично выполнена.
Зачем это спрашивают: Сильный ответ показывает, что вызов cancel не завершает произвольную работу принудительно, и объясняет, где ставить проверки.
Actor защищает изолированное состояние от одновременного доступа, но во время приостановки метода в actor может войти другая задача.
- Состояние, прочитанное до await, может устареть к моменту продолжения.
- До приостановки я сохраняю только неизменяемые входы, а после await повторно проверяю состояние actor.
- При обновлении баланса я не использую последовательность чтение, await, запись, если финальная запись заново не проверяет текущий баланс.
Зачем это спрашивают: Интервьюер проверяет понимание reentrancy в actor, а не предположение, что весь async-метод выполняется атомарно.
Sendable помечает значения, которые можно безопасно передавать между доменами конкурентности без общего изменяемого состояния.
- Неизменяемые value types обычно удовлетворяют контракту, если все их свойства тоже Sendable.
- Изменяемому reference type нужны синхронизация, изоляция actor или переработка, прежде чем он сможет честно соответствовать Sendable.
- unchecked Sendable отключает проверку компилятора, поэтому я оставляю его только для типов, чья потокобезопасность обеспечена и описана в другом месте.
Зачем это спрашивают: Интервьюер проверяет, воспринимаете ли вы Sendable как контракт безопасности, а не аннотацию для подавления предупреждений.
Я изолирую UI-состояние и его изменения в MainActor, а сетевую и тяжёлую CPU-работу оставляю вне этой изоляции.
- Пометка ViewModel через MainActor позволяет компилятору проверять изменения публикуемого состояния.
- Асинхронный сетевой вызов может приостановиться без блокировки actor, но синхронная обработка изображения всё равно заморозит UI.
- Я выношу парсинг в async-сервис вне MainActor с Sendable-входом и результатом; Task.detached использую только для обоснованной неструктурированной CPU-работы, захватываю только Sendable-значения и передаю отмену.
Зачем это спрашивают: Сильный ответ отличает изоляцию actor от фонового выполнения и не помещает тяжёлую синхронную работу в UI actor.
Structured concurrency привязывает время жизни и ошибки дочерних корутин к явному родительскому scope.
- coroutineScope ждёт всех детей и отменяет соседние задачи при ошибке одной из них.
- supervisorScope изолирует ошибки дочерних задач, когда независимая работа должна продолжиться.
- UI-работу я запускаю в viewModelScope, а не GlobalScope, чтобы очистка ViewModel отменяла незавершённые jobs.
Зачем это спрашивают: Интервьюер ожидает понимание жизненного цикла и распространения ошибок, а не только знание launch и async.
Я выбираю dispatcher по характеру работы и переключаю его только на границе, которая владеет этой работой.
- Dispatchers.Main обслуживает UI-состояние, Dispatchers.IO подходит для блокирующих файловых или legacy-вызовов базы, а Default для CPU-преобразований.
- Репозиторий может применить withContext для блокирующей зависимости, чтобы вызывающему коду не требовалось знать её требования к выполнению.
- Я не оборачиваю естественно приостанавливаемые вызовы Retrofit или Room в IO, потому что эти API уже не блокируют вызывающий поток.
Зачем это спрашивают: Сильный ответ распределяет dispatchers по типу нагрузки и избегает лишних или управляемых вызывающей стороной переключений.
Я позволяю CancellationException распространяться и добавляю кооперативную проверку отмены в длительную работу, реализованную в приложении.
- Suspending functions обычно проверяют отмену, а CPU-цикл должен периодически вызывать ensureActive или yield.
- Широкий catch для Exception обязан повторно выбросить CancellationException, иначе ненужная UI-работа продолжит жить.
- Очистка выполняется в finally, а NonCancellable используется только для короткой обязательной приостановки, например закрытия транзакции.
Зачем это спрашивают: Интервьюер проверяет, сохраняет ли обработка ошибок семантику отмены корутин и владение их жизненным циклом.
Обычный Flow чаще всего холодный и запускает работу для каждого collector, а StateFlow горячий и всегда содержит текущее значение.
- Холодный Flow подходит для запроса или потока, чей producer должен запускаться при сборе.
- ViewModel публикует StateFlow для состояния экрана, потому что пересозданный collector сразу получает последнее значение.
- stateIn с lifecycle-aware политикой sharing преобразует исходный Flow и не держит дорогую работу активной бесконечно.
Зачем это спрашивают: Интервьюер оценивает понимание времени жизни потока, replay и владения состоянием, а не запоминание API.
В Combine subscriber запрашивает demand, а подписка активна только пока сохраняется её cancellation token.
- Operators передают demand выше по цепочке, хотя некоторые publishers могут буферизовать данные или производить их быстрее получателя.
- sink возвращает AnyCancellable, который я храню у владельца и освобождаю, когда тот должен прекратить наблюдение.
- Closures захватывают self слабо, если владелец хранит cancellable, иначе цепочка подписки может образовать цикл.
Зачем это спрашивают: Сильный ответ охватывает семантику backpressure и распространённый цикл владения через AnyCancellable.
Закрытые вопросы
- 21
Как определить владельца состояния в SwiftUI?
swiftuiswift - 22
Как спроектировать разрешение конфликтов для записи CloudKit, изменённой на нескольких устройствах?
conflictdesign - 23
Как спроектировать типобезопасную границу между TypeScript в React Native и нативным кодом?
reactdesigntypescript - 24
Как поднимать state в Jetpack Compose, не делая API экрана неудобным?
composeapijetpack - 25
Как спроектировать тестируемую навигацию для декларативных мобильных UI?
navigationdesign - 26
Как для новой фичи checkout выбрать между VIPER, MVVM и границами Clean Architecture?
architecture-patternsclean-architecture - 27
Как встроить UIKit в SwiftUI без циклов обновления и двойного владения состоянием?
uikitswiftuiownership - 28
Что меняется в New Architecture React Native и как в неё вписываются Hermes, Fabric и TurboModules?
reactarchitecture - 29
Чем отличаются Widget, Element и RenderObject во Flutter?
flutterwidgetscustom-rendering - 30
Как спроектировать интеграцию нативной возможности через Flutter platform channels?
flutterdesign - 31
Как безопасно предоставить Objective-C framework коду на Swift?
swift - 32
Как организовать Core Data contexts для отзывчивого чтения и безопасной фоновой записи?
responsiveandroid-componentsswift - 33
Как планировать миграции схемы для Room и Core Data?
schemamigrationsroom - 34
Что должна определять политика мобильного кеша помимо time-to-live?
caching - 35
Как смоделировать надёжную очередь синхронизации для офлайн-изменений?
data-structures - 36
Почему операции offline-синхронизации должны быть идемпотентными?
idempotency - 37
Как выбрать стратегию разрешения конфликтов для офлайн-редактирования?
- 38
Какие гарантии и ограничения нужно учитывать при использовании WorkManager?
designjetpackautolayout - 39
Какие ограничения определяют дизайн фоновой работы iOS через BGTaskScheduler?
design - 40
Как tombstones помогают синхронизировать офлайн-удаления?
- 41
Как мобильное приложение должно управлять жизненным циклом OAuth access token и refresh token?
oauthtokenslifecycle - 42
Как использовать Keychain, Android Keystore и CryptoKit для локальной защиты секретов?
secretssecure-storage - 43
Каковы компромиссы certificate pinning в мобильном приложении?
- 44
Что биометрическая аутентификация на самом деле доказывает приложению?
auth - 45
Как проектировать сбор мобильных данных с учётом приватности, а не только запрашивать разрешения?
designpermissions - 46
Как проверить deep link перед переходом к защищённому контенту?
validationnavigation - 47
Как приложение должно обрабатывать жизненный цикл push notification token?
lifecycleandroid-componentstokens - 48
Как управлять владением камерой и сенсорами в мобильных жизненных циклах?
ownershiplifecycle - 49
Как спроектировать надёжный процесс покупки в мобильном приложении?
design - 50
Как поддерживать согласованность normalized GraphQL cache после mutations и pagination?
graphqlpaginationnormalization - 51
При холодном запуске iOS deep link на заказ 42 сначала открывает нужный экран, но затем восстановленный путь NavigationStack заменяет его экраном из прошлой сессии. Как исправить гонку запуска?
navigationsessions - 52
Экран Jetpack Compose рекомпозируется около 120 раз в секунду во время работы таймера, а прокрутка падает до 40 fps. Как локализовать и остановить шторм рекомпозиций?
composeconcurrencyjetpack - 53
Покупка через StoreKit 2 проходит успешно, но приложение завершается до выдачи premium-доступа на backend; после перезапуска пользователь остаётся без доступа, а транзакция не завершена. Как безопасно восстановить покупку?
transactions - 54
Долгоживущий эффект Jetpack Compose отправляет элемент, выбранный при первом открытии экрана, а не текущий. Как диагностировать и исправить устаревшую лямбду?
composelambdajetpack - 55
После десяти открытий и закрытий экрана остаются десять сетевых опросов и 30 МБ удерживаемой памяти. Как найти и исправить утечку корутин?
memorycoroutines - 56
После рефакторинга на Swift concurrency экран поиска зависает на 900 мс, потому что парсинг проходит через view model, изолированную главным актором. Как исправить границу акторов?
concurrencyswiftui - 57
Каждый вход на экран профиля iOS добавляет одного активного подписчика Combine, а deinit не вызывается и после 20 посещений. Как найти и убрать утечку подписки?
combine - 58
График React Native получает через границу JS/native JSON объёмом 18 МБ каждые 200 мс, и UI падает до 25 fps. Что вы измените?
react - 59
Ввод в одно поле Flutter-формы перестраивает всю вкладку из 300 виджетов и вызывает заметные пропуски кадров. Как уменьшить область rebuild?
formsflutterwidgets - 60
Flutter-плагин работает в debug, но Android release-сборка выдаёт MissingPluginException для одного метода, тогда как iOS работает. Как отлаживать сбой platform channel?
flutterplatform - 61
Offline-очередь повторяется после перезапуска приложения и создаёт дубликаты расходов в 1,8% синхронизаций. Как сделать путь записи безопасным?
data-structures - 62
Два устройства редактируют одну offline-заметку, затем подключаются и незаметно перезаписывают изменения друг друга. Как реализовать и проверить обработку конфликтов?
- 63
Android-обновление переводит Room с версии 8 на 10 и падает только у пользователей, пропустивших версию 9. Как исправить и предотвратить разрыв миграции?
databasemigrationsroom - 64
Обновление модели Core Data вызывает launch-crash у 0,7% хранилищ, созданных три релиза назад, а lightweight migration не может вывести mapping. Каков план восстановления?
migrationsmodelingcore-data - 65
Неатомарный batch CloudKit сохраняет 30 задач; один элемент возвращает CKError.serverRecordChanged внутри partialFailure, но приложение повторяет все 30 записей и перезаписывает заголовок коллеги. Как исправить синхронизацию?
batchconcurrency - 66
При смене фильтра во время infinite scroll иногда добавляется старая страница, появляются дубликаты и неверный next cursor. Как устранить гонку пагинации?
deduppagination - 67
Загрузка файла на 200 МБ доходит до 70%, ОС завершает фоновое задание, и приложение каждый раз начинает с нуля. Как сделать прогресс устойчивым?
jobs - 68
Некоторые пользователи дважды получают один push о заказе из FCM и видят два уведомления. Как убрать дубликаты, не потеряв настоящее обновление?
queriesandroid-components - 69
Защищённый deep link при холодном старте теряется после входа, а специально составленная ссылка открывает внутренний admin route. Как исправить обе проблемы?
navigation - 70
После пяти снимков по 12 МП память вырастает примерно на 240 МБ, и устройства с малым объёмом памяти падают. Как диагностировать и исправить camera pipeline?
memoryci-cd - 71
Production p95 холодного старта равен 3,2 секунды, а тёплый старт занимает 600 мс. Как найти и убрать работу, характерную только для холодного старта?
- 72
В feed фиксируется 22% медленных кадров, когда пользователи быстро прокручивают 1000 строк смешанного контента. Как уменьшить list jank?
rendering - 73
TurboModule с Codegen передаёт кадры камеры в React Native; после 20 открытий и закрытий анализатора RSS растёт на 15 МБ за цикл, хотя heap Hermes возвращается к исходному уровню. Как найти и исправить утечку?
data-structuresstreamsreact - 74
Play Console показывает шестисекундный ANR при возобновлении приложения, а главный поток находится внутри Room-запроса и JSON-декодирования. Как решить проблему?
queriesroomconcurrency - 75
После включения live location приложение расходует 12% батареи в час в простое. Как найти и остановить расход?
- 76
Во время сбоя backend каждое устройство отправляет около 30 повторов в секунду и замедляет восстановление. Как остановить retry storm?
resilience - 77
После трёх циклов background и foreground одно устройство держит четыре WebSocket-соединения и получает каждое сообщение четыре раза. Как исправить lifecycle?
websocketslifecycle - 78
Парсинг JSON-ответа на 25 МБ занимает 1,4 секунды и временно удваивает память приложения. Как снизить стоимость?
memory - 79
Локальный поиск по 100 000 записей занимает 600 мс и блокирует каждое нажатие клавиши. Как оптимизировать запрос к базе?
databasequeriesoptimization - 80
Новый analytics SDK добавляет 800 мс к холодному старту и повышает долю падений с 0,2% до 0,5%. Как локализовать риск интеграции?
- 81
У pricing-фичи 85% покрытия строк, но в production всё равно уходят ошибки округления и устаревшего состояния. Как перестроить стратегию unit-тестов?
coveragetest-strategyhypothesis-testing - 82
Семь последних ошибок возникли на границе API cache и Room, хотя repository тщательно замоканы в unit-тестах. Какие интеграционные тесты вы добавите?
unitintegrationintegration-testing - 83
Мобильный E2E suite идёт 48 минут, падает в 14% запусков и блокирует релизы. Как перестроить его, не потеряв покрытие checkout?
e2ecoverage - 84
Асинхронный тест view model падает примерно в одном из 20 запусков, потому что ждёт 500 мс перед проверкой. Как сделать его детерминированным?
asyncconcurrencyui - 85
Релиз проходит на современных флагманах, но падает на Android 10 с малым объёмом памяти и ломает layout на iPhone SE. Как определить device matrix?
memory - 86
Backend меняет nullable-поле на отсутствующее, и старые версии приложения падают при декодировании. Как contract tests предотвратят такой релиз?
contract - 87
iOS archive в CI падает на чистых runners, signing profile истекает через неделю, а разработчики зависят от локального Keychain. Как стабилизировать signing?
secure-storage - 88
Fastlane-релиз загрузил staging flavor в production с неверными bundle ID и скриншотами. Как предотвратить повторение?
- 89
Во время staged release доля падений растёт с 0,2% до 1,1% на rollout 10%. Какие действия и gates вы используете?
- 90
Чистая mobile-сборка занимает 22 минуты, тёплые сборки колеблются от 4 до 15 минут, а cache hit rate равен лишь 35%. Как улучшить build performance?
caching - 91
Workflow в Bitrise открывает ключ App Store Connect и пароль для подписи сборкам pull request, где сторонний step может прочитать окружение. Как локализовать инцидент и изолировать подпись?
incidentspasswordsdependencies - 92
Когда 20 запросов одновременно получают 401, приложение запускает 20 refresh, перезаписывает новые токены и иногда разлогинивает пользователя. Как исправить гонку?
tokens - 93
После обновления серверного сертификата TLS начинает сбоить только на Android API 24-25, потому что сервер не выдаёт intermediate certificate. Как диагностировать и исправить проблему?
tlsapi - 94
После изменения набора Face ID платящие пользователи навсегда блокируются, потому что biometric key аннулирован, а fallback не появляется. Как исправить flow?
- 95
Timeout платежа с последующим retry вызывает двойное списание в 0,15% покупок. Как сделать mobile flow идемпотентным?
resilienceidempotency - 96
Только 38% пользователей разрешают камеру, а review отклоняет permission text: он говорит о необходимости камеры, но не называет цель. Как улучшить flow?
permissions - 97
VoiceOver и TalkBack пропускают итог заказа, читают три кнопки как просто button, а focus прыгает после валидации. Как исправить и проверить экран?
validation - 98
Новый checkout под feature flag повышает долю падений с 0,2% до 2%, но часть offline-пользователей после rollback сохраняет закешированное enabled-значение. Как сделать kill switch безопаснее?
feature-flagsrollbackdesign - 99
На code review PR сохраняет Activity в singleton для запуска payment SDK, а LeakCanary показывает одну удерживаемую Activity на каждый checkout. Как провести review?
code-reviewandroid-components - 100
Парсер push, написанный junior-разработчиком, использует forced casts и вызывает 0,4% падений при запуске из уведомления с необязательным полем. Как вы поможете junior-разработчику самостоятельно найти причину и исправить проблему?
mentoringandroid-componentsswift