Процесс технического собеседования для разработчиков может различаться в зависимости от компании и конкретной роли. Какие этапы все же стоит ожидать: Телефонный скрининг. Первым шагом в процессе собеседования обычно является телефонный разговор с рекрутером. Это короткая беседа, которая проводится для того, чтобы узнать больше о вашем прошлом опыте. Вы можете задать HR менеджеру любые первоначальные вопросы о текущей роли и компании, которые могут у вас возникнуть. Встречается практически всегда, но даже первый созвон с HR‑ом может превратиться в технический скрининг. Рекрутер по списку заранее заготовленных вопросов будет неловко спрашивать вас о вещах, которые ему самому могут быть совершенно неизвестны, а вы должны попасть как можно точнее в ответ. Иногда становится неловко даже вам, но, как правило, вопросы достаточно базовые и не должны вызывать проблем. Техническое собеседование. Следующим шагом обычно является техническое собеседование, которое может принимать различные формы. Некоторые компании могут попросить вас выполнить тестовое задание дома или пройти техническую онлайн‑оценку, в то время как другие могут провести Live coding сессию или попросить вас показать предыдущий проект, над которым вы работали. Техническое собеседование — это возможность для интервьюера оценить ваши технические навыки, способность решать проблемы и проверить, насколько хорошо вы знакомы с инструментами и фреймворками разработки. Onsite встреча. Если вы пройдете техническое собеседование, вас могут пригласить на собеседование в офис. Она может включать встречи с различными членами команды, разработчиками, менеджерами по продуктам и дизайнерами. У вас появится возможность узнать больше о культуре компании и динамике команды, а команда сможет оценить вашу квалификацию при помощи дополнительных сессий. Например, решение алгоритмический задач на whiteboard или system design интервью. Поведенческое интервью: Вас также могут попросить принять участие в поведенческом интервью. Больше свойственно для западных компаний особенно FAANG MANGA. Это возможность для интервьюера узнать больше о вашем стиле работы, навыках общения, и о том, как вы справляетесь с различными ситуациями. Вас могут попросить привести примеры того, как вы справлялись с трудными или конфликтными ситуациями в прошлом. Найти хорошо собранный список вопросов для теоретической части интервью не составит труда. Выделить для себя топ 50-150 довольно просто: Top iOS Interview Questions and Answers Over 150 iOS interview questions Об этом написано немало статей и даже книг. В крупных международных компаниях ищут «инженеров». То есть универсальных солдат, которые могут поменять свой стек за пару недель. Live coding интервью в таких компаниях ориентировано на решение алгоритмических задач. Как правило, они не зависят от операционной системы или выбранного языка программирования.  Компании поменьше, особенно компании в СНГ странах предпочитают более предметный подход. Если это собеседование на iOS разработчика, то задачи будут в рамках этой платформы.  Попробуем разобрать наиболее популярные задачи, которые вы можете встретить на live coding этапе. Время, затраченное на решение таких задач, не должно превышать 30 минут, а их количество, обычно, ограничивается 1–2 задачами за одно интервью. Swift дает нам возможность работать в многопоточной среде. Но что произойдет, если два потока попытаются записать в одну и ту же коллекцию одновременно или один поток читает значение, а другой поток записывает значение? Получаем ли мы самое последнее значение из коллекции? Ответ на эти вопросы заключается в том, что вы не можете одновременно писать в одну и ту же область памяти или читать во время записи из той же области памяти. Тогда возникает вопрос, а зачем нам нужна не потокобезопасные объекты? Ответ: потому что потокобезопасная коллекция/переменная/свойство замедляет выполнение программы. Вы можете столкнуться с этой проблемой, при работе с коллекциями например: Array, Dictionary, Set Чтобы получить потокобезопасную коллекцию, нам нужно определить геттер и сеттер. Всякий раз, когда мы устанавливаем какую‑либо коллекцию, мы должны убедиться, что никакие другие потоки в настоящее время не получают доступ к ней.  Для решения этой проблемы можно применить разные подходы. Например: NSLock, NSRecursiveLock, pthread_mutex_t, DispatchQueue. Давайте рассмотрим некоторые из них. На интервью вам могут предложить шаблон будущего класса: Чтобы получить и установить значение словаря, можно использовать параллельную очередь. Синхронный вызов выдаст значение из словаря, если оно существует, а для установки значения в словарь используется асинхронный вызов с барьером. Блок с флагом .barrier будет действовать как реальный барьер. Все блоки, которые были отправлены на выполнение до барьерной операции, будут завершены, и только после этого будет выполнен блок с барьером. Все блоки, отправленные после прохождения барьера, не будут запущены до тех пор, пока блок с барьером не будет завершен. В этой реализации класс ThreadSafeDictionary хранит приватные ссылки на словарь для хранения своих внутренних данных и очереди для синхронизации доступа к этим данным. Методы removeAll, removeValue(forKey:), получают доступ к внутреннему словарю через queue.async(flags: .barrier), a contains(key:) и count через queue.sync тем самым, обеспечивая потокобезопасный доступ и модификацию словаря. Для следующего способа решения этой же задачи мы используем @propertyWrapper, который обеспечивает атомарные операции в Swift с использованием механизма os_unfair_lock: Структура Atomic имеет единственное дженерик свойство value, которое представляет тип обернутого значения; Свойство value содержит обернутое значение, а свойство lock содержит объект os_unfair_lock_t, который используется для синхронизации доступа к значению; Свойство wrappedValue имеет get и set, которые используют функции os_unfair_lock_lock и os_unfair_lock_unlock для блокирования и разблокирования доступа к свойству соответственно; Ключевое слово defer используется для обеспечения того, чтобы блокировка всегда снималась, даже если в геттере или сеттере возникает ошибка. Это замыкание будет вызвано после выполнения геттера или сеттера. В этом подходе реализация потокобезопасного словаря будет выглядеть следующим образом: Данная задача позволяет интервьюерам проверить ваши знания по работе с многопоточностью, потокобезопасностью и инструментами синхронизации. Детальней познакомиться с отличиями и скоростью выполнения этих реализаций можно по ссылке. Часто встречается задача на понимание как работать с DispatchGroup и для чего она может пригодится на практике. Рассмотрим проблему:  Требуется реализовать метод performWorks; Замыкание complete должно вызываться по завершению всех операций; Последовательность элементов внутри замыкания должна оставаться как в исходном массиве; Метод performWork требует много ресурсов. Возможное решение: Создаем объект типа DispatchGroup и кастомную параллельную очередь; Копируем исходный массив в локальную переменную — чтобы в последствии использовать для сохранения порядка; Устанавливаем notify для группы. Когда все операции выйдут из группы, будет вызвано замыкание complete; В цикле выполняем операции. Код внутри цикла исполняется асинхронно на параллельной очереди т.к. по условию метод performWork требует много ресурсов; group.enter() вызывается до вызова метода performWork; Чтобы обезопасить доступ к результирующему массиву, следует записывать в него значения используя флаг .barrier; После сохранения результата выполнения замыкания выходим из группы с помощью group.leave(). Навыки работы с DispatchGroup необходимы для работы с набором ассинхронных операций. Также вы можете попробовать решить эту задачу используя фреймворк Combine и оператор collect. В Swift функции высшего порядка — это функции, которые принимают одну или несколько функций в качестве аргументов или возвращают функцию в качестве результата. Это мощный инструмент для работы с коллекциями, поскольку они позволяют писать лаконичный и выразительный код, который можно легко настроить для различных вариантов использования. Вот некоторые из них и их назначение: map — преобразует каждый элемент коллекции с помощью замыкания и возвращает новую коллекцию того же размера; filter — возвращает новую коллекцию, содержащую только те элементы, которые удовлетворяют заданному предикату;  reduce — объединяет элементы коллекции в одно значение, используя замыкание, которое принимает два аргумента: накопленное значение и следующий элемент коллекции; compactMap — возвращает новую коллекцию с ненулевыми результатами вызова замыкания для каждого элемента исходной коллекции. На интервью вас могут попросить написать реализацию этих методов. Рассмотрим пример: Как может выглядеть ваше решение: В этом примере функция map определена как расширение типа Collection. Функция map принимает замыкание, которое преобразует элементы типа Element в элементы типа T. Замыкание помечено ключевым словом throws, чтобы указать, что оно может вызвать ошибку. Сама функция map также помечена ключевым словом rethrows, чтобы указать, что она может повторно вызывать любые ошибки, вызванные замыканием. Внутри функции map создается новый пустой массив типа  для хранения преобразованных элементов. Метод reserveCapacity вызывается у нового массива, чтобы предварительно выделить достаточно места для всех элементов исходного массива, что может повысить производительность. Затем цикл for используется для перебора каждого элемента исходного массива. Замыкание применяется к каждому элементу с помощью ключевого слова try, а преобразованный элемент добавляется к новому массиву. Наконец, новый массив возвращается. Остальные функций высшего порядка попробуйте реализовать сами =) Предположим, есть протокол Request с associated type. Он позволяет нам скрывать различные формы запросов данных (например, сетевые запросы, запросы к базе данных и работы с кэшом) за одним унифицированным интерфейсом: Задача реализовать класс‑очередь, которая позволяет работать с разными запросами по правилам FIFO.  Исходные данные выглядят следующим образом: Есть класс RequestQueue и метод add(_ :). Нужно реализовать всю логику класса. Мы не можем просто так передать параметр типа Request т.к. он имеет assoсiated type. В таком случае, для хранения запросов в очереди нам придется применить технику TypeErasure.  Замыкание typeErased будет захватывать как запрос (request), так и его обработчик (handler), не раскрывая никакой информации об этом типе за его пределами, обеспечивая полное стирание типа (type erasure); Замыкания сохраняются в массив с типом ; Метод performNextIfNeeded проверяет, нет ли выполнения в данный момент и есть ли в массиве еще запросы на очереди; Если запрос есть, выставляем флаг isPerformingRequest в true, достаем из массива следующий запрос на очереди и выполняем его, вызывая замыкание. В Swift 5.6 и 5.7 произошло много изменений относительно концепции type erasure, и теперь она выглядит более нативно. Подробности можно посмотреть здесь и здесь. Давайте посмотрим, как решение задачи с очередью запросов может выглядеть при использовании ключевых слов any и some: Теперь нам не нужно хранить в массиве typeErased замыкания. Ключевые слова some и any позволяют использовать type erasure технику с помощью opaque type  и existential type соответственно.  Заметьте, что класс RequestQueue не является потокобезопасным, что в реальном проекте, скорее всего, вызвало бы проблемы. Список приведенных мною задач отражает темы, которые важны для любого iOS разработчика. Цель проверить знания и умения работать с той или иной технологией. Разумеется, список неполный и может быть добавлен и содержать другие вариации задач. Вы можете использовать эти примеры при проведении интервью в своих компаниях или для подготовки к собеседованиям. Если даете эти примеры, постарайтесь, прежде всего, понять, как кандидат размышляет во время решения, а не получить точную имплементацию с соблюдением синтаксиса языка, особенно если вы используете IDE без подсказок. Будьте снисходительны, принимая во внимание стресс и волнение на интервью, а также тот факт, что решение кандидата может отличаться стилистически от вашего. Чтобы узнать больше об iOS разработке и мире IT подписывайтесь на мой телеграм канал https://t.me/ios_mobile_developer Senior iOS разработчик. iOS Team lead