api.ezedev.ru - это бесплатная площадка для тренировки выполнения сетевых запросов на языке Swift. Ниже представлено описание методов сервиса, а также примеры выполнения запросов с использованием различных инструментов. Площадка будет расти и дополняться новыми методами и примерами. Скачать готовый код можно тут.

Как работать с этой страницей
  1. Ознакомьтесь с описанием методов в категориях Public и Authorized (см. ниже)
  2. Поиграйте со свагером, в нем не нужно писать код. Для удобства есть инструкция
  3. Изучите раздел URLSession и попробуйте отправить запросы из своего проекта
  4. Привяжите отправку запроса к нажатию на кнопку в приложении или другому действию пользователя
  5. По желанию можно дополнительно изучить и другие способы отправки запросов (async/await, Alamofire, Moya)
  6. При возникновении вопросов не стесняйтесь писать в наш чат
Public
В этой категории представлены методы, не требующие авторизации

/makeHandshake - простой метод для "рукопожатия" с сервером.

/messages - отправка данных на сервер в POST-запросе и получение этих данных в виде комбинированной строки.

/login - авторизация по номеру телефона и паролю. В ответе приходит токен, необходимый для выполнения запросов из категории authorized. В настоящий момент доступен только один статичный пользователь: 79876543210, qwerty.

Authorized
Список методов, требующих предварительной авторизации и получения токена

/current_profile - получение текущего профиля пользователя. В хедер запроса нужно поместить токен, полученный в запросе авторизации.

Способы отправки запросов
В настоящий момент на сайте описаны примеры для работы со следующими технологиями:
Swagger UI
Выполнение запросов с использованием консоли Swagger
Swagger предоставляет удобный веб-интерфейс для проверки работы методов бэкенда. В настоящий момент доступны две категории методов - публичные (доступные без токена авторизации) и авторизованные (для них нужно получить токен с помощью метода /login)
Раскрыв любой метод, можно посмотреть информацию о требуемом формате входных и выходных данных
Нажав на кнопку Try it out, вы получите возможность редактировать входные данные и отправить запрос, нажав на Execute
После выполнения запроса вы увидите ответ сервера
В правом верхнем углу есть кнопка Authorize, которая открывает окно для ввода и сохранения токена, получаемого из метода /login
Здесь нужно ввести токен в формате "Bearer token", где token - тот самый токен авторизации, полученный в методе /login
URLSession
Нативный инструмент Apple для выполнения сетевых запросов. Скачать весь код
Начнем с метода /makeHandshake. Для начала создадим структуру, по которой будем парсить ответ от сервера

struct HandshakeData: Decodable {
    let message: String
}
Далее напишем код запроса. Это довольно простой метод, который возвращает статичную текстовую строку. После получения ответа от сервера происходит парсинг по структуре HandshakeData

func makeHandshake() {
    let url = URL(string: "https://api.ezedev.ru/makeHandshake")!
    let request = URLRequest(url: url)
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        if let data, let handshake = try? JSONDecoder().decode(HandshakeData.self, from: data) {
            print(handshake.message)
        } else {
            print("Error =)")
        }
    }
    task.resume()
}
Для работы следующего метода потребуются два структуры данных: PostMessageData служит для кодирования тела запроса, а PostMessageResponse нужна для декодирования ответа

struct PostMessageData: Encodable {
    let firstname: String
    let lastname: String
    let age: Int
    let weight: Double
}

struct PostMessageResponse: Decodable {
    let success: Bool
    let message: String?
    let error: String?
}
Для работы метода нам понадобятся четыре значения, которые функция принимает на входе: firstname, lastname, age, weight. Устанавливаем тип запроса POST, т.к. по умолчанию запрос имеет тип GET. Также устанавливаем для хедера Content-Type значение application/json, чтобы получить возможность отправлять параметры в теле запроса. Далее формируем то самое тело запроса. Инициализируем message с помощью входных параметров, кодируем и кладем в запрос, после чего выполняем этот запрос и парсим ответ по структуре PostMessageResponse

func postMessage(firstname: String, lastname: String, age: Int, weight: Double) {
    let url = URL(string: "https://api.ezedev.ru/messages")!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"

    request.setValue("application/json", forHTTPHeaderField: "Content-Type")

    let message = PostMessageData(firstname: firstname, lastname: lastname, age: age, weight: weight)
    let messageData = try? JSONEncoder().encode(message)
    request.httpBody = messageData
    
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        if let data, let postResponse = try? JSONDecoder().decode(PostMessageResponse.self, from: data) {
            print(postResponse.message ?? "")
        } else {
            print("Error =)")
        }
    }
    task.resume()
}
Метод /login нужен для получения токена авторизации по номеру телефона и паролю. Обращаясь к другим запросам, требующим авторизации, мы будем использовать уже не логин/пароль, а токен, который получим в этом запросе. Помимо номера телефона и пароля, этот метод принимает на вход замыкание completion, с помощью которого мы узнаем о завершении работы метода. Установим тип метода POST, преобразуем строку с параметрами в тип Data и выполним запрос. Если получится распарсить ответ сервера по структуре LoginResponseData, распечатаем полученный токен и вернем его в completion

func login(phone: String, password: String, completion: @escaping (String?) -> Void) {
    let url = URL(string: "https://api.ezedev.ru/login")!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"

    let body = "phone=\(phone)&password=\(password)".data(using: .utf8)
    request.httpBody = body

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        if let data, let loginData = try? JSONDecoder().decode(LoginResponseData.self, from: data) {
            print(loginData.accessToken)
            completion(loginData.accessToken)
        } else {
            print("Error =)")
        }
    }
    task.resume()
}
Структура LoginResponseData

struct LoginResponseData: Codable {
    let accessToken, tokenType: String

    enum CodingKeys: String, CodingKey {
        case accessToken = "access_token"
        case tokenType = "token_type"
    }
}
Вызов метода может выглядеть так. В настоящий момент в системе есть только один статичный пользователь, поэтому нужно использовать его данные для входа.

login(phone: "79876543210", password: "qwerty") { token in
   self.token = token ?? ""
}
При этом должна быть объявлена переменная, которая будет хранить полученный токен, чтобы другие запросы могли к ней обращаться

var token: String = ""
Метод /current_profile служит для получения данных о текущем пользователе. В хедер запроса нужно положить токен авторизации, полученный в методе /login

func getProfile() {
    let url = URL(string: "https://api.ezedev.ru/current_profile")!
    var request = URLRequest(url: url)
    let tokenHeaderValue = "Bearer \(token)"
    request.setValue(tokenHeaderValue, forHTTPHeaderField: "Authorization")
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        if let data, let profile = try? JSONDecoder().decode(ProfileData.self, from: data) {
            print(profile.name)
            print(profile.age)
        } else {
            print("Error =)")
        }
    }
    task.resume()
}
Ответ сервера будет парситься по структуре ProfileData

struct ProfileData: Decodable {
    let name: String
    let age: Int
}
При этом получить данные о профиле текущего пользователя можно только при условии, что уже получен токен авторизации. Проще всего это сделать, вызвав метод getProfile() сразу после записи токена

login(phone: "79876543210", password: "qwerty") { token in
    self.token = token ?? ""
    self.getProfile()
}
async/await
Пример использования async/await
Alamofire
Использование сторонней библиотеки Alamofire
Moya
Использование сторонней библиотеки Moya
Создадим файл APIType.swift. В enum APIType опишем типы запросов, применимых к актуальному бэкенду. В extension APIType пропишем свойства, различающиеся для каждого запроса.

import Foundation
import Moya

enum APIType {
    case makeHandshake
    case messages(PostMessageData)
    case login(String, String)
    case currentProfile

    static var provider = MoyaProvider<Self>()
}

extension APIType: TargetType, AccessTokenAuthorizable {
    var baseURL: URL {
        URL(string: "https://api.ezedev.ru")!
    }

    var path: String {
        switch self {
        case .makeHandshake:
            return "/makeHandshake"
        case .messages:
            return "/messages"
        case .login:
            return "/login"
        case .currentProfile:
            return "/current_profile"
        }
    }

    var method: Moya.Method {
        switch self {
        case .makeHandshake:
            return .get
        case .messages:
            return .post
        case .login:
            return .post
        case .currentProfile:
            return .get
        }
    }

    var task: Moya.Task {
        switch self {
        case .makeHandshake:
            return .requestPlain
        case .messages(let value):
            return .requestJSONEncodable(value)
        case .login(let phone, let password):
            return .requestParameters(
                parameters: [
                    "phone": phone,
                    "password": password
                ],
                encoding: URLEncoding.default
            )
        case .currentProfile:
            return .requestPlain
        }
    }

    var headers: [String: String]? {
        nil
    }

    var authorizationType: AuthorizationType? {
        switch self {
        case .currentProfile:
            return .bearer

        default:
            return nil
        }
    }
}

Создадим файл Service.swift и напишем структуру MoyaService.

import Moya

public struct MoyaService {
    let provider: MoyaProvider<APIType>

    init(accessToken: String) {
        self.provider = .init(
            plugins: [
                AccessTokenPlugin(
                    tokenClosure: { _ in
                        return accessToken
                    }
               )
            ]
        )
    }
}
Пример отправки всех запросов в классе ViewController.

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        makeHandshake()
        postMessage(firstname: "eze", lastname: "dev", age: 1, weight: 1.5)
        login(phone: "79876543210", password: "qwerty") { token in
            self.token = token
            self.getProfile()
        }
    }

    var token: String? = ""

    func makeHandshake() {
        let moyaService = MoyaService(accessToken: "")

        moyaService.provider.request(.makeHandshake) { result in
            switch result {
            case .success(let response):
                if let value = try? JSONDecoder().decode(HandshakeData.self, from: response.data) {
                    print(value.message)
                }
            case .failure(let error):
                print(error)
            }
        }
    }

    func postMessage(firstname: String, lastname: String, age: Int, weight: Double) {
        let data = PostMessageData(firstname: firstname, lastname: lastname, age: age, weight: weight)
        let moyaService = MoyaService(accessToken: "")

        moyaService.provider.request(.messages(data)) { result in
            switch result {
            case .success(let response):
                if let value = try? JSONDecoder().decode(PostMessageResponse.self, from: response.data) {
                    print(value.message ?? "")
                }
            case .failure(let error):
                print(error)
            }
        }
    }

    func login(phone: String, password: String, completion: @escaping (String?) -> Void) {
        let moyaService = MoyaService(accessToken: "")

        moyaService.provider.request(.login(phone, password)) { result in
            switch result {
            case .success(let response):
                if let response = try? JSONDecoder().decode(LoginResponseData.self, from: response.data) {
                    completion(response.accessToken)
                }
            case .failure(let error):
                print(error)
            }
        }
    }

    func getProfile() {
        guard let token else { return }
        let moyaService = MoyaService(accessToken: token)
        moyaService.provider.request(.currentProfile) { result in
            switch result {
            case .success(let response):
                if let response = try? JSONDecoder().decode(ProfileData.self, from: response.data) {
                    print(response.name)
                    print(response.age)
                }
            case .failure(let error):
                print(error)
            }
        }
    }
}