Приходилось ли вам реализовывать сетевой слой в iOS-приложении?

«Приходилось ли вам реализовывать сетевой слой в iOS-приложении?» — вопрос из категории Сети, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, реализовывал абстракцию сетевого слоя на основе URLSession с использованием протоколов для гибкости и тестируемости.

Ключевые компоненты:

  1. Протокол сервиса — определяет контракт для работы с сетью.
  2. Модель Endpoint — инкапсулирует данные запроса (путь, метод, параметры).
  3. Конкретная реализация — выполняет запросы и обрабатывает ответы.
// 1. Протокол для инъекции зависимостей и тестирования
protocol NetworkServiceProtocol {
    func request<T: Decodable>(_ endpoint: Endpoint) async throws -> T
}

// 2. Модель, описывающая конечную точку API
struct Endpoint {
    let path: String
    let method: HTTPMethod // GET, POST и т.д.
    let parameters: [String: Any]?
    let headers: [String: String]?
}

// 3. Реализация с использованием async/await
final class DefaultNetworkService: NetworkServiceProtocol {
    private let session: URLSession
    private let decoder: JSONDecoder

    init(session: URLSession = .shared, decoder: JSONDecoder = JSONDecoder()) {
        self.session = session
        self.decoder = decoder
    }

    func request<T: Decodable>(_ endpoint: Endpoint) async throws -> T {
        // Построение URLRequest на основе Endpoint
        let request = try buildRequest(from: endpoint)

        // Выполнение запроса
        let (data, response) = try await session.data(for: request)

        // Валидация HTTP-ответа
        guard let httpResponse = response as? HTTPURLResponse,
              (200...299).contains(httpResponse.statusCode) else {
            throw NetworkError.invalidServerResponse
        }

        // Декодирование данных в модель
        return try decoder.decode(T.self, from: data)
    }

    private func buildRequest(from endpoint: Endpoint) throws -> URLRequest { ... }
}

Преимущества такого подхода:

  • Тестируемость: Легко подменить NetworkServiceProtocol моком в юнит-тестах.
  • Гибкость: Отделение логики запроса от бизнес-логики.
  • Безопасность: Централизованная обработка ошибок сети и декодирования.
  • Современность: Использование async/await для чистого и читаемого асинхронного кода.