Серия статей о работе с сетью состоит из трёх частей:
- Часть 1 — Базовые запросы
- Часть 2 — Протокол Codable
- Часть 3 — Оптимизация файлов и кода (эта статья)
Для примера будем рассматривать работу с вот этим сервисом, который позволяет делать запросы без авторизации.
Первая конструкция, которую мы напишем для структурирования запросов к API — это enum со всеми возможными запросами:
Первая конструкция, которую мы напишем для структурирования запросов к API — это enum со всеми возможными запросами:
enum ApiType {
case login
case getUsers
case getPosts
case getAlbums
}
Здесь мы видим 4 вида запросов:
Также в этот enum необходимо добавить 4 обязательные переменные:
baseURL
Этот URL является общим для всех запросов. Вставим этот код в наш enum:
- Авторизация пользователя (придумали сами для примера);
- Получение списка пользователей;
- Получение списка постов;
- Получение списка альбомов.
Также в этот enum необходимо добавить 4 обязательные переменные:
- baseURL — общий URL для всех запросов;
- headers — хедеры запроса;
- path — путь запроса;
- request — сам запрос.
baseURL
Этот URL является общим для всех запросов. Вставим этот код в наш enum:
var baseURL: URL {
return URL(string: “https://jsonplaceholder.typicode.com/")!
}
headers
Хедеры, как и все последующие переменные, меняются в зависимости от типа запроса: авторизационные хедеры должны содержать токен авторизации, а остальные запросы могут содержать пустые хедеры. Говоря проще, для запроса login хедеры должны быть вида [“auth-token”: “12345”], а для всех остальных запросов — вида [:]. Запишем это в коде:
Хедеры, как и все последующие переменные, меняются в зависимости от типа запроса: авторизационные хедеры должны содержать токен авторизации, а остальные запросы могут содержать пустые хедеры. Говоря проще, для запроса login хедеры должны быть вида [“auth-token”: “12345”], а для всех остальных запросов — вида [:]. Запишем это в коде:
var headers: [String: String] {
switch self {
case .login: return [“auth-token”: “12345”]
default: return [:]
}
}
path
Эта переменная должна содержать путь до запроса за исключением той его части, которая содержится в baseURL:
Эта переменная должна содержать путь до запроса за исключением той его части, которая содержится в baseURL:
var path: String {
switch self {
case .login: return “login”
case .getUsers: return “users”
case .getPosts: return “posts”
case .getAlbums: return “albums”
}
}
request
Переменную request нужно построить исходя из нашего запроса. Для начала нужно построить URL, состоящий из baseURL и path. Далее нужно создать сам запрос и присвоить ему хедеры из переменной headers. После этого можно провести индивидуальную настройку для каждого запроса. В нашем случае мы лишь определяем метод запроса:
Переменную request нужно построить исходя из нашего запроса. Для начала нужно построить URL, состоящий из baseURL и path. Далее нужно создать сам запрос и присвоить ему хедеры из переменной headers. После этого можно провести индивидуальную настройку для каждого запроса. В нашем случае мы лишь определяем метод запроса:
var request: URLRequest {
let url = URL(string: path, relativeTo: baseURL)!
var request = URLRequest(url: url)
request.allHTTPHeaderFields = headers
switch self {
case .login:
request.httpMethod = “GET”
return request
default:
request.httpMethod = “GET”
return request
}
}
Вообще говоря, делать разбивку между .login и остальными запросами в данном случае не обязательно, но мы поступили так для примера и наглядности.
Итоговый ApiType должны выглядеть так:
Итоговый ApiType должны выглядеть так:
Отлично, теперь приступим к настройке моделей, по которым мы будем парсить приходящие данные. На сайте с документациейперейдем по ссылке, чтобы увидеть формат приходящих данных. Скопируем текст целиком и отправим его в Quicktype. Убедимся, что мы сделали все поля опциональными (это нужно, т.к. мы не уверены, что сервис всегда будет отправлять нам все поля. А вдруг у какого-то пользователя не указан возраст):
Quicktype сгенерировал для нас несколько структур:
Скопируем код структур и перенесем её в новый файл User.swift. После этого у нас должна быть примерно такая структура файлов:
Проделаем аналогичное действие для остальных запросов. В результате мы получим следующую картину:
Отлично, теперь можно приступать к методам для реализации запросов. Доступ к веб-сервису может понадобиться в разных частях приложения. Для этого удобно создать единую точку доступа к сервису, к которой будут обращаться разные модули внутри вашего приложения. Самое простое решение сводится к использованию синглтона. Создадим класс ApiManager и реализуем его единственный экземпляр, который назовем shared:
class ApiManager {
static let shared = ApiManager()
}
Напишем в нём функцию для получения списка пользователей:
Разберемся, что происходит в каждой строчке:
Проверить правильность работы кода можно в любом месте приложения, просто вызвав:
- 59: Объявляем функцию с completion-блоком и атрибутом @escaping. Этот атрибут говорит о том, что нам нужно дождаться ответа от сервера.
- 60: Создаем запрос с помощью ApiType, который мы спроектировали ранее.
- 61: Создаем dataTask с запросом, созданным на предыдущем шаге. Получаем от сервера данные (data), ответ (response) и код ошибки (error).
- 62: Проверяем, что data не равна nil, а затем проверяем, что data можно распарсить по структуре Users.
- 63. Если все хорошо, то возвращаем список пользователей.
- 65: Если data=nil, или данные не получилось распарсить, что возвращаем пустой массив пользователей.
- 68: Запускаем dataTask по загрузке данных.
Проверить правильность работы кода можно в любом месте приложения, просто вызвав:
ApiManager.shared.getUsers { users in
print(users)
}
Итоговый класс ApiManager со всеми необходимыми методами должен выглядеть вот так:
Примерно похожую, но немного усложненную структуру файлов и классов я использую в своих проектах. Вы можете организовать работу с API так, либо любым другим удобным способом.
Навыки работы с API можно отработать на вот этих сервисах, а если что-то не получается, то проблемой можно поделиться в нашем чате.
Всем пока!
Навыки работы с API можно отработать на вот этих сервисах, а если что-то не получается, то проблемой можно поделиться в нашем чате.
Всем пока!