Skip to content

Вопросы на собеседовании: iOS-разработчик

100 реальных вопросов с образцовыми ответами и пояснениями для уровня Middle.

Смотреть пример резюме: iOS-разработчик

Тренировка флешкарточками

Интервальное повторение · Hunter Pass

Вопросы

swiftvalue-semanticsswiftui

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

  • SwiftUI может создавать новые значения View при изменении входных данных, поэтому struct View нельзя считать постоянным объектом UI.
  • Property wrappers связывают описание с хранилищем, которым владеет фреймворк, другой объект или environment.
  • Изменение состояния инвалидирует зависимые части иерархии и заставляет SwiftUI заново вычислить их body.

Зачем это спрашивают: Интервьюер проверяет понимание декларативного рендеринга, а не отношение к SwiftUI View как к контроллеру UIKit.

ownershipswiftswiftui

@State связывает с идентичностью View локальное хранилище небольшого изменяемого значения, которое служит источником истины.

  • SwiftUI владеет хранилищем и сохраняет его между новыми экземплярами одной идентичности View.
  • Изменение wrapped value инвалидирует вычисления body, которые его читают.
  • Свойство стоит делать private, если оно является деталью реализации, а его начальное значение не следует использовать как внешний канал синхронизации.

Зачем это спрашивают: Сильный ответ объясняет хранилище под управлением фреймворка, идентичность View и то, почему @State не подходит для общего состояния.

swiftuiswift

@Binding предоставляет доступ на чтение и запись к состоянию, которым владеет другой объект, не принимая владение его хранилищем.

  • Родитель обычно передаёт дочернему View $property, если тот должен менять значение.
  • Чтение binding возвращает текущее исходное значение, а запись обновляет тот же источник.
  • Binding также можно создать с собственными замыканиями get и set, чтобы адаптировать существующую модель состояния.

Зачем это спрашивают: Интервьюер проверяет, отличаете ли вы заимствованный доступ к изменению от владения состоянием.

ObservableObject предоставляет уведомления об изменениях, а @Published отправляет их перед изменением отмеченного свойства.

  • ObservableObject предоставляет publisher objectWillChange, который используется наблюдением SwiftUI.
  • @Published создаёт publisher отдельного свойства и одновременно участвует в общем сигнале изменения объекта.
  • Для UI изменения должны происходить на подходящем actor, поэтому наблюдаемую модель часто изолируют с помощью @MainActor.

Зачем это спрашивают: Хороший ответ связывает инвалидацию объекта, publishers свойств и изоляцию UI.

swiftuiownershipswift

@ObservedObject предполагает, что View наблюдает за ObservableObject, за время жизни которого отвечает внешний владелец.

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

Зачем это спрашивают: Интервьюер оценивает, отделяете ли вы наблюдение от создания объекта и владения им.

swiftui

@StateObject сохраняет ObservableObject, когда идентичность View создаёт этот объект и владеет им.

  • SwiftUI инициализирует объект один раз для этой идентичности, а не при каждом новом создании значения View.
  • Уведомления об изменении объекта инвалидируют зависимый UI так же, как с @ObservedObject.
  • Хранилище прекращает существование, когда владеющая идентичность View покидает иерархию.

Зачем это спрашивают: Сильный ответ связывает @StateObject со стабильным владением ссылочным объектом и идентичностью View.

swiftui

Замена @StateObject на @ObservedObject убирает управляемое SwiftUI владение объектом со стороны View.

  • Объект, созданный прямо в @ObservedObject, может пересоздаваться при появлении новых значений View.
  • Повторная инициализация может сбросить состояние и заново запустить подписки или другую начальную работу.
  • Если экземпляр уже передаёт внешний владелец, @ObservedObject является правильным выбором, потому что второй владелец исказил бы поток данных.

Зачем это спрашивают: Интервьюер проверяет, можете ли вы выбирать wrapper на основе времени жизни, а не синтаксиса.

swiftui

@EnvironmentObject получает общий ObservableObject, который предок передал через environment SwiftUI.

  • Он избавляет от явной передачи одного объекта через каждый промежуточный View.
  • Поиск выполняется по типу, поэтому в иерархии должна существовать подходящая инъекция environmentObject.
  • Wrapper подходит действительно общим зависимостям, но чрезмерное использование скрывает требования View и усложняет previews или тесты.

Зачем это спрашивают: Хороший ответ охватывает поиск по типу и компромисс между удобной передачей и явными зависимостями.

swiftui

@Environment читает значение по ключу environment, а @EnvironmentObject находит наблюдаемый ссылочный объект по типу.

  • К environment values относятся системные входные данные, например locale, color scheme и действие dismiss.
  • Собственные определения EnvironmentKey могут передавать небольшие значения или зависимости.
  • @EnvironmentObject специально наблюдает за внедрённым ObservableObject и требует наличия этого объекта выше в иерархии.

Зачем это спрашивают: Интервьюер проверяет, отличаете ли вы environment values с поиском по ключу от наблюдаемых объектов с поиском по типу.

swiftuiswift

SwiftUI связывает хранилище состояния со структурной или явной идентичностью View, а не с конкретным экземпляром структуры.

  • При сохранении идентичности хранилище @State и @StateObject переживает повторное вычисление body.
  • Изменение идентичности из-за другой ветки, позиции или id может удалить старое хранилище и создать новое.
  • Поэтому стабильная идентичность влияет на правильность списков, условных иерархий и дочерних Views с состоянием.

Зачем это спрашивают: Сильный ответ объясняет сбросы состояния правилами идентичности, а не произвольными перерисовками.

combine

Publisher создаёт типизированную последовательность, Subscriber получает её, а Subscription связывает их и управляет спросом и отменой.

  • Publisher объявляет типы Output и Failure.
  • Subscriber получает подписку, затем от нуля до нескольких значений и не более одного сигнала завершения.
  • Subscription позволяет подписчику запрашивать нужное число значений и отменять дальнейшую доставку.

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

combineci-cd

Каждый Publisher в Combine указывает тип отправляемых значений и тип ошибки, с которой он может завершиться.

  • Publisher, который не может завершиться ошибкой, использует Never как тип Failure.
  • Операторы должны сохранять или намеренно преобразовывать совместимые типы Output и Failure.
  • mapError, setFailureType, catch и replaceError адаптируют канал ошибок для следующих звеньев цепочки.

Зачем это спрашивают: Хороший ответ показывает рассуждение на уровне типов о значениях и завершающих ошибках pipeline.

combine

AnyCancellable представляет владение отменой, и его освобождение отменяет связанную подписку.

  • sink и assign обычно возвращают AnyCancellable для созданного подписчика.
  • Хранение в свойстве или Set поддерживает активность подписки в течение жизни владельца.
  • Явная отмена или освобождение хранилища прекращает дальнейшую доставку и разрывает цепочку подписки.

Зачем это спрашивают: Интервьюер оценивает, понимаете ли вы время жизни подписки как обычное владение.

PassthroughSubject передаёт только новые значения, а CurrentValueSubject также хранит и предоставляет текущее значение.

  • Новый подписчик PassthroughSubject ничего не получает до следующего send.
  • Новый подписчик CurrentValueSubject сразу получает текущее значение, а затем последующие обновления.
  • Оба типа позволяют императивно отправлять значения и сигнал завершения в декларативную цепочку.

Зачем это спрашивают: Сильный ответ отличает поток событий от потока, похожего на состояние.

combine

map напрямую преобразует каждое значение, а flatMap превращает каждое значение в новый Publisher и объединяет эти внутренние потоки.

  • map сохраняет отношение одного значения к одному и не вводит новый асинхронный поток.
  • flatMap полезен, когда входное значение запускает работу, представленную Publisher.
  • flatMap не отменяет старые внутренние publishers автоматически, поэтому switchToLatest выбирают отдельно, когда активным должен оставаться только новейший поток.

Зачем это спрашивают: Интервьюер проверяет понимание разворачивания publishers и отсутствие путаницы flatMap с обычным преобразованием значения.

combine

combineLatest отправляет сочетание последних значений после первого значения от каждого источника, а zip соединяет значения по порядку их появления.

  • combineLatest реагирует на обновление любого upstream и повторно использует последнее значение другого.
  • zip ждёт по одному несопоставленному значению с каждой стороны и потребляет их как пару.
  • Выбор зависит от того, представляет ли связь текущее состояние или попарное сочетание событий.

Зачем это спрашивают: Хороший ответ объясняет семантику отправки, а не просто называет два оператора объединения.

combine

subscribe(on:) влияет на место создания подписки и выполнения upstream-работы, а receive(on:) меняет scheduler для downstream-доставки.

  • subscribe(on:) влияет на операции подписки, запроса значений и отмены выше по цепочке.
  • receive(on:) доставляет последующие значения и завершение на указанном scheduler.
  • Этапы, работающие с UI, обычно получают значения на main scheduler, но лишние переключения scheduler не стоит разбрасывать по цепочке.

Зачем это спрашивают: Интервьюер проверяет, умеете ли вы размещать границы планирования с учётом upstream-работы и downstream-потребителей.

combine

eraseToAnyPublisher скрывает конкретную цепочку publisher за стабильным типом AnyPublisher.

  • Цепочки операторов имеют длинные обобщённые типы, зависящие от реализации, которые обычно не нужны вызывающему коду.
  • Стирание типа не позволяет изменениям реализации проникать в сигнатуру API.
  • Оно сохраняет типы Output и Failure, но убирает доступ к операциям конкретного publisher.

Зачем это спрашивают: Сильный ответ связывает стирание типа с границами API, а не считает его обязательным для каждого pipeline.

asyncswiftconcurrency

async отмечает функцию, которая может приостановиться, а await отмечает вызов, где текущая задача может приостановиться до получения асинхронного результата.

  • Приостановка задачи не блокирует лежащий в основе поток во время ожидания.
  • Async-функция может вернуть значение или выбросить ошибку, а сочетание возможностей записывается как async throws.
  • await отмечает возможную точку приостановки, но не гарантирует ни фактическую приостановку, ни переключение потока.

Зачем это спрашивают: Интервьюер проверяет, отличаете ли вы приостановку task от блокировки и планирования потока.

asyncconcurrencyswift

async let создаёт дочернюю задачу в текущей области видимости для значения, которое можно вычислять одновременно с другой дочерней работой.

  • Несколько объявлений async let запускают дочерние операции до ожидания их значений.
  • Чтение значения требует await и передаёт ошибку, если дочернее выражение может её выбросить.
  • Незавершённые дочерние задачи автоматически ожидаются или отменяются до выхода из внешней области видимости.

Зачем это спрашивают: Хороший ответ определяет async let как structured concurrency для фиксированного числа дочерних результатов.

Закрытые вопросы

  • 21

    Чем withTaskGroup отличается от async let?

    asyncconcurrency
  • 22

    Какой контекст наследует Task { } в Swift concurrency?

    concurrencyswiftownership
  • 23

    Чем Task.detached отличается от Task { }?

  • 24

    Как работает отмена в Swift concurrency?

    concurrencyswiftresilience
  • 25

    Какую гарантию изоляции предоставляет actor в Swift?

    swiftconcurrency
  • 26

    Что гарантирует @MainActor?

  • 27

    Что такое reentrancy у actor в Swift?

    swiftconcurrency
  • 28

    Что означает Sendable в Swift concurrency?

    concurrencyswift
  • 29

    Как Automatic Reference Counting управляет памятью в Swift?

    memoryswift
  • 30

    Чем типы-значения отличаются от ссылочных типов с точки зрения владения?

    value-semanticsownership
  • 31

    Как возникает цикл сильных ссылок?

    memory
  • 32

    Какой семантикой обладает weak-ссылка в Swift?

    swift
  • 33

    Чем unowned отличается от weak в Swift?

    memoryswift
  • 34

    Как escaping closure может создать retain cycle со своим владельцем?

    closuresmemory
  • 35

    Почему свойства delegate обычно объявляют weak?

    delegationprotocols
  • 36

    Что представляют собой Encodable, Decodable и Codable?

    codable
  • 37

    Когда компилятор может синтезировать реализацию Codable?

    codable
  • 38

    Чем отличаются keyed, unkeyed и single-value containers в Codable?

    containerscodable
  • 39

    Чем serial DispatchQueue отличается от concurrent DispatchQueue?

    concurrency
  • 40

    Чем sync dispatch отличается от async dispatch в GCD?

    asyncconcurrency
  • 41

    Что предоставляют DispatchGroup и quality of service очереди?

    data-structures
  • 42

    Что означает protocol-oriented design в Swift?

    typingswiftprotocols
  • 43

    Чем отличается dispatch для требований протокола и методов только из extension?

    typingprotocols
  • 44

    Что такое associated type в протоколе Swift?

    typingswiftprotocols
  • 45

    Как ограничения типов и where clauses улучшают обобщённый API в Swift?

    genericsswiftapi
  • 46

    Чем some Protocol отличается от any Protocol в Swift?

    typingswiftprotocols
  • 47

    Какую проблему решает type erasure в Swift?

    swift
  • 48

    Какая ответственность принадлежит Model, View и ViewModel в MVVM?

    architecture-patterns
  • 49

    Как SwiftUI View может наблюдать за MVVM ViewModel?

    swiftuiswiftarchitecture-patterns
  • 50

    Чем не должна владеть MVVM ViewModel?

    architecture-patterns
  • 51

    Как бы вы реализовали экран списка товаров для состояний загрузки, заполненного и пустого списка, а также ошибки?

  • 52

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

    debounceswiftuiswift
  • 53

    Как бы вы реализовали надёжную бесконечную прокрутку ленты?

  • 54

    Как бы вы построили список с офлайн-режимом на основе Core Data?

    core-data
  • 55

    Как бы вы разрешили конфликт офлайн-изменений, если та же запись изменилась на сервере?

  • 56

    Как бы вы реализовали загрузку изображений для быстро прокручиваемой ленты?

  • 57

    Как бы вы реализовали многошаговую форму SwiftUI с проверкой данных и отправкой?

    formsswiftuivalidation
  • 58

    Как бы вы реализовали deep link на вложенный экран, требующий авторизации?

  • 59

    Как бы вы открывали нужный раздел по push-уведомлению при разных состояниях жизненного цикла приложения?

  • 60

    Как бы вы добавили оптимистичное обновление кнопки избранного в SwiftUI?

    swiftuilockingswift
  • 61

    Дочерний экран SwiftUI теряет состояние ViewModel при обновлении родительского экрана; как бы вы нашли и исправили проблему?

    swiftuiswift
  • 62

    Как бы вы разделили редактируемое состояние между несколькими экранами SwiftUI без появления несогласованных копий?

    swiftuiswift
  • 63

    Где бы вы запускали асинхронные побочные эффекты на экране SwiftUI?

    asyncswiftconcurrency
  • 64

    Как бы вы обеспечили потокобезопасность ViewModel в SwiftUI при загрузке и преобразовании данных?

    concurrencyswiftswiftui
  • 65

    Как бы вы восстановили SwiftUI NavigationStack после перезапуска приложения?

    swiftuinavigationswift
  • 66

    Как бы вы встроили существующий контроллер UIKit в SwiftUI и сделали обновления предсказуемыми?

    uikitswiftuilearning
  • 67

    Как бы вы встроили функцию на SwiftUI в существующий навигационный сценарий UIKit?

    uikitswiftuinavigation
  • 68

    Как бы вы проверили доступность при реализации собственного элемента управления в SwiftUI?

    a11yswiftuiswift
  • 69

    Закрытый экран не деинициализируется; как бы вы нашли retain cycle?

    memory
  • 70

    Как бы вы отлаживали retain cycle с Combine sink во ViewModel?

    combinememory
  • 71

    Сбой в рабочей версии приложения виден только в несимволицированных отчётах; что бы вы сделали?

  • 72

    Как бы вы исследовали сбой EXC_BAD_ACCESS в коде Swift?

    swift
  • 73

    Как бы вы диагностировали обновления UIKit вне главного потока?

    uikitconcurrency
  • 74

    Как бы вы исследовали гонку данных в общем кэше в памяти?

    cachingmemory
  • 75

    Как бы вы отлаживали периодический сбой index out of range в представлении коллекции?

    viewsindexes
  • 76

    Пользователи сообщают, что приложение зависает на несколько секунд без сбоя; как бы вы это исследовали?

    scheduling
  • 77

    Как бы вы отлаживали взаимную блокировку между старым кодом GCD и кодом с @MainActor?

    lockingconcurrency
  • 78

    Как бы вы профилировали и снижали высокую загрузку процессора в функции iOS-приложения?

  • 79

    Список UIKit плохо прокручивается при появлении изображений и текста; как бы вы его оптимизировали?

    uikitoptimization
  • 80

    Как бы вы снизили скачки потребления памяти при загрузке большого числа фотографий?

    memory
  • 81

    Экран SwiftUI слишком часто пересчитывается и перерисовывается; как бы вы нашли и сократили лишние обновления?

    swiftuiswift
  • 82

    Как бы вы ускорили медленный холодный запуск приложения?

  • 83

    Как бы вы спроектировали тестируемый сетевой слой на URLSession с типизированными конечными точками?

    designendpointsnetworking
  • 84

    Как бы вы предотвратили одновременное обновление истёкшего токена доступа несколькими неудачными запросами?

    tokens
  • 85

    Как бы вы определяли, какие сетевые запросы можно автоматически повторять?

  • 86

    Как бы вы передали отмену с исчезнувшего экрана SwiftUI в URLSession?

    swiftuiresilienceswift
  • 87

    Как бы вы преобразовывали сетевые сбои в полезные для пользователя ошибки?

  • 88

    Как бы вы добавили HTTP-кэширование в клиент на URLSession?

    cachinghttpnetworking
  • 89

    Как бы вы внедряли разные сетевые реализации в функции iOS-приложения без глобального экземпляра?

  • 90

    Как бы вы отлаживали запрос API, который работает в браузере, но завершается ошибкой в iOS-приложении?

    api
  • 91

    Как бы вы написали модульный тест для MVVM ViewModel с @MainActor?

    architecture-patternsunit
  • 92

    Как бы вы тестировали отмену и завершение асинхронных операций не по порядку?

    asyncswiftconcurrency
  • 93

    Когда в тестах вы бы использовали URLProtocol вместо тестовой реализации сетевого протокола?

    typingprotocolstesting
  • 94

    Как бы вы тестировали репозиторий Core Data, не затрагивая постоянные данные пользователя?

    core-data
  • 95

    Как бы вы сделали UI-тест XCTest для входа в приложение менее нестабильным?

    flaky
  • 96

    Как бы вы выбрали тесты для новой функции офлайн-поиска?

    testing
  • 97

    Тест проходит локально, но периодически завершается ошибкой в CI; как бы вы его исследовали?

  • 98

    Как бы вы добавили регрессионный тест для сбоя в рабочей версии из-за некорректных данных API?

    regressionapi
  • 99

    Как бы вы ускорили медленный набор iOS-тестов без потери полезного покрытия?

    coverage
  • 100

    Как бы вы спроектировали и проверили офлайн-ленту SwiftUI с пагинацией, изображениями и deep link?

    swiftuidesignvalidation