Вопрос проверяет умение построить простой, но гибкий сетевой клиент на нативных средствах iOS.
Создают обёртку вокруг URLSession, выделяют Router для формирования запросов (URLRequest), NetworkManager для выполнения задач и парсинга JSON через Codable. Вынесение common-логики (таймаут, заголовки, обработка ошибок) в один класс упрощает поддержку.
Определение эндпоинтов (Router):
enum API {
case getUsers, getPosts(userId: Int)
var urlRequest: URLRequest {
var url = URL(string: "https://api.example.com")!
switch self {
case .getUsers:
url.appendPathComponent("/users")
case .getPosts(let id):
url.appendPathComponent("/posts")
url = URL(string: "\(url)?userId=\(id)")!
}
var req = URLRequest(url: url)
req.httpMethod = "GET"
req.setValue("application/json", forHTTPHeaderField: "Accept")
return req
}
}NetworkManager:
class NetworkManager {
private let session = URLSession.shared
func request<T: Decodable>(_ api: API, completion: @escaping (Result<T, Error>) -> Void) {
let task = session.dataTask(with: api.urlRequest) { data, res, err in
if let e = err { return completion(.failure(e)) }
guard let d = data else { return /* ошибка */ }
do {
let obj = try JSONDecoder().decode(T.self, from: d)
completion(.success(obj))
} catch {
completion(.failure(error))
}
}
task.resume()
}
}Использование:
let nm = NetworkManager()
nm.request(.getUsers) { (result: Result<[User], Error>) in
switch result {
case .success(let users): print(users)
case .failure(let err): print(err)
}
}Вывод:
Такой слой легко тестировать, расширять новыми эндпоинтами и поддерживать без дополнительных зависимостей.