Как реализовать добавление ячеек в таблицу из внешнего кода без изменения таблицы
Что проверяют
Речь о принципе открытости/закрытости: таблица показывает новые типы ячеек, а её код при этом не правится. Интервьюер смотрит, уйдёте ли вы от switch по типам в cellForRowAt, и почти всегда уточняет, как вставить строки без reloadData.
Как отвечать
Таблица знает только абстракцию, а конкретные ячейки приносит внешний код:
- протокол модели ячейки: например
CellModelсreuseIdentifier, классом ячейки и методомconfigure(_ cell: UITableViewCell). Источник данных хранит[any CellModel]и для строки делаетdequeueReusableCell(withIdentifier:for:)иconfigure; - регистрация: внешний модуль регистрирует свой класс через
register(_:forCellReuseIdentifier:), либо источник данных регистрирует тип из модели при первом появлении; - источник данных отдельно от контроллера: объект с
UITableViewDataSourceподключается композицией и подменяется в тестах; - API добавления: метод вроде
append(_ models:), который согласованно обновляет массив и таблицу.
Путей обновления два. Классический: сначала меняете массив, потом внутри performBatchUpdates вызываете insertRows(at:with:). Современный: UITableViewDiffableDataSource и NSDiffableDataSourceSnapshot. Внешний код добавляет элементы, вы применяете снапшот через apply(_:animatingDifferences:), разницу система вычислит сама. Для содержимого стандартных ячеек с iOS 14 есть UIListContentConfiguration, свои конфигурации делаются через UIContentConfiguration.
Хорошо добавить, что тот же подход разбивает огромный экран на независимые секции, у каждой свой поставщик ячеек.
Где спотыкаются
Оставляют switch model.type в источнике данных. Каждый новый тип снова требует правки таблицы, задача не решена.
Вызывают insertRows без изменения массива или в другом порядке. Число строк расходится с данными, и приложение падает с NSInternalInconsistencyException про неверное количество строк в секции.
В diffable-источнике делают неуникальные идентификаторы или считают Hashable по изменяемым полям. Дубли дают падение или предупреждение, а смена поля превращается в удаление и вставку вместо обновления.