> Как реализовать удаление нескольких элементов при ограничении бэкенда на один запрос на удаление (iOS, Swift)
Уровень: senior · Роль: mobile · Категория: Технические вопросы
Компании: Совкомбанк
Стек: iOS, Swift
> Пример ответа
Короткий ответ
Основная стратегия - агрегация удалений на клиенте: собираем идентификаторы элементов, отправляем один batch-запрос на кастомный endpoint или используем последовательные запросы с ограничением параллелизма. Если бэкенд жёстко ограничивает один запрос на удаление, применяем очередь операций с retry и обработкой частичных ошибок. Для iOS это означает аккуратную работу с URLSession, управление состоянием и синхронизацию с UI через Combine или async/await.
Подробное объяснение
Ограничение бэкенда на один запрос на удаление - это типичный случай, когда сервер не поддерживает batch-операции. Возможные причины: архитектурные ограничения, требования к аудиту, лимиты на размер payload. Клиент должен адаптироваться, не ломая пользовательский сценарий.
Ключевые подходы:
-
Последовательное удаление - отправляем запросы по одному, дожидаясь ответа. Просто, но медленно при большом количестве элементов. Требует обработки ошибок: если третий из десяти не удалился, нужно решить, продолжать ли.
-
Параллельное удаление с ограничением - используем
TaskGroupилиOperationQueueсmaxConcurrentOperationCount. Быстрее, но нужно учитывать нагрузку на сервер и возможные rate limits. -
Кастомный batch endpoint - если есть возможность договориться с бэкендом, создаём endpoint, принимающий массив ID. Это идеальный вариант, но часто недоступен.
-
Очередь с persistency - сохраняем pending-удаления в локальную базу (Core Data, SwiftData, Realm). При восстановлении соединения отправляем по одному. Это даёт надёжность и офлайн-режим.
Для iOS важно:
- Использовать
async/awaitдля читаемого кода, но не забывать проTaskи отмену при уходе с экрана. - Обрабатывать
URLErrorи HTTP-статусы: 404 (уже удалён), 409 (конфликт), 429 (rate limit). - Обновлять UI только после подтверждения с сервера, либо оптимистично с возможностью отката.
На практике
Сценарий: пользователь выбирает 10 элементов и жмёт "Удалить". Бэкенд принимает только DELETE /items/{id}.
Реализация:
- Собираем массив
[Item.ID]. - Показываем прогресс (например, "Удалено 3 из 10").
- Отправляем запросы последовательно или с ограничением параллелизма (обычно 2-3).
- При ошибке на конкретном элементе - помечаем его как failed, продолжаем остальные.
- По завершении показываем итог: "Удалено 8, 2 не удалось" с кнопкой "Повторить".
Важно: не блокировать UI на всё время операции. Использовать ProgressView или анимированный индикатор. Также продумать отмену - если пользователь ушёл с экрана, операции должны отмениться через Task.cancel().
Пример кода
SWIFTfunc deleteItems(ids: [Int], maxConcurrent: Int = 3) async throws -> DeleteResult {var deleted: [Int] = []var failed: [Int: Error] = [:]try await withThrowingTaskGroup(of: (Int, Error?).self) { group invar iterator = ids.makeIterator()var active = 0// Заполняем группу до лимитаwhile active < maxConcurrent, let id = iterator.next() {group.addTask {do {try await self.deleteItem(id: id)return (id, nil)} catch {return (id, error)}}active += 1}// Обрабатываем результаты и добавляем новые задачиfor try await (id, error) in group {active -= 1if let error = error {failed[id] = error} else {deleted.append(id)}if let nextId = iterator.next() {group.addTask {do {try await self.deleteItem(id: nextId)return (nextId, nil)} catch {return (nextId, error)}}active += 1}}}return DeleteResult(deleted: deleted, failed: failed)}private func deleteItem(id: Int) async throws {var request = URLRequest(url: URL(string: "https://api.example.com/items/\(id)")!)request.httpMethod = "DELETE"let (_, response) = try await URLSession.shared.data(for: request)guard let http = response as? HTTPURLResponse, http.statusCode == 200 else {throw URLError(.badServerResponse)}}
Как отвечать на собеседовании
Начни с уточнения: "Есть ли возможность добавить batch endpoint?" - это покажет, что ты думаешь о системном решении, а не только о клиенте. Затем опиши trade-off между последовательным и параллельным подходом, упомяни rate limits и идемпотентность. Обязательно скажи про обработку частичных ошибок и пользовательский опыт - это ключевое для senior. Если спросят про конкретные технологии, упомяни TaskGroup, AsyncThrowingStream или OperationQueue для ограничения параллелизма. В конце добавь про тестирование: как проверить сценарий с падением сети на середине.
Что проверяет интервьюер
Интервьюер оценивает:
- Понимание ограничений бэкенда и умение адаптироваться.
- Способность проектировать надёжные клиент-серверные взаимодействия.
- Знание конкурентности в Swift:
async/await,TaskGroup, отмены. - Внимание к UX: прогресс, ошибки, повторные попытки.
- Умение говорить о trade-off, а не просто выдавать код.
Типичные ошибки
- Отправка всех запросов одновременно без ограничения - сервер может упасть или забанить клиента.
- Игнорирование частичных ошибок: если один элемент не удалился, не падать и не откатывать всё.
- Блокировка main thread при ожидании ответов - использовать
async/awaitправильно. - Не учитывать идемпотентность: повторный запрос на уже удалённый элемент должен быть безопасным (обрабатывать 404).
- Забывать про отмену операций при уходе с экрана - это приводит к утечкам и лишним запросам.
- Не показывать пользователю прогресс и итог - он думает, что всё зависло.
> Похожие задачи по mobile
Какие альтернативы NSOperation существуют для отслеживания выполнения задач в iOS
Как показать пользователю, что все элементы успешно удалены
Какие проблемы с многопоточностью существуют, например race condition, data race, starvation, priority inversion, deadlock
В чем разница паттернов Bridge и Proxy
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью