HTTP-запросы в SwiftUI

Часть 2. Запрос данных JSON

При написании приложения иногда необходимо получить внешнюю информацию, например изображения или информацию в формате JSON. Ожидая этих данных, вы должны убедиться, что приложение не зависает, и действовать соответствующим образом в случае сбоя соединения.

В этой серии руководств я хочу показать вам, как вы можете запрашивать различные данные (изображения, текст и т. д.) и отображать их в своем приложении SwiftUI. В первой части этой серии руководств мы рассмотрели, как загружать и отображать изображения по URL-адресу в вашем приложении SwiftUI. В этой части мы расскажем, как запрашивать и анализировать данные JSON, которые часто возвращаются API.

Предварительные условия

Если вы хотите следовать инструкциям, у вас должен быть установлен XCode на вашем MacBook и иметь базовое понимание SwiftUI и Swift.

Кроме того, полезно общее понимание HTTP-запросов и JSON.

Настраивать

Если вы еще этого не сделали, создайте новый проект XCode и откройте ContentView или, если вы работаете над существующим проектом, представление, из которого вы хотите загрузить дополнительные данные.

Запрос и отображение данных JSON

Большинство современных API используют JSON для получения и отправки данных, и существуют всевозможные API практически для любого варианта использования. В этом уроке мы будем использовать этот шуточный API здесь: https://jokeapi.dev/. Для использования этого API вам не нужен ключ API, что делает его идеальным для тестирования.

На главной странице веб-сайта вы можете напрямую опробовать API и даже настроить некоторые аргументы. Затем он также показывает URL-адрес, необходимый для выполнения запроса. Мы хотим показывать шутки по программированию в нашем приложении, поэтому нам нужно будет сделать GET-запрос к https://v2.jokeapi.dev/joke/Programming.

Прежде чем писать реальный код на Swift, мне часто проще выполнить быстрый запрос с помощью Curl или, если API более сложный, с Postman, чтобы увидеть полученный JSON. В этом случае веб-страница также отображает JSON непосредственно при нажатии кнопки «Отправить запрос».

Создание нового класса

Теперь, когда мы знаем, что нам нужно сделать запрос GET к определенному URL-адресу и нам не нужны какие-либо конкретные заголовки, мы можем работать с кодом Swift. Начнем с создания нового файла Swift, который будет содержать код запроса. Нажмите ⌘+N, чтобы создать новый файл, и выберите Swift File в диалоговом окне.

Назовите файл как-нибудь описательно, например JokesApi, и нажмите «Создать». В этом файле мы создаем новый класс JokesApi и добавляем функцию getRandomJoke(), которая будет возвращать строку для нашей шутки. Мы также добавим переменную, хранящую URL-адрес.

import Foundation

class JokesApi {
    let jokeUrl = URL(string: "https://v2.jokeapi.dev/joke/Programming")!
    
    func getRandomJoke() {
        // TODO
    }
}

Прежде чем мы сможем выполнить фактический запрос, нам нужен объект Joke, в котором мы можем проанализировать JSON и который мы также можем вернуть.

Создание объекта, представляющего JSON

Снова создайте новый файл, нажав ⌘+N, и выберите Swift File в диалоговом окне. Назовите это Joke и нажмите «Создать». Это будет объект, который должен сопоставить ключи JSON. К счастью, нам не нужно сопоставлять все значения, и мы можем выбрать только те, которые нам нужны.

Технически нам нужна только ключевая шутка, но другие ключи, такие как ошибка или тип, могут представлять интерес позже.

import Foundation

struct Joke: Codable, Identifiable {
    let id: Int
    let error: Bool
    let category: String
    let type: String
    let joke: String
}

Обязательно наследуйте от Codeable и Identifying, чтобы Swift мог напрямую декодировать строку JSON в этот объект.

Выполнение GET-запроса

Теперь мы можем выполнить фактический запрос в ранее созданной функции getRandomJoke(). Для выполнения запроса мы будем использовать объект URLSession, который можно настроить, но он также имеет одноэлементный общий сеанс без настройки для базовых запросов, что идеально подходит для нашего варианта использования.

Если вы хотите использовать объект URLSession для дополнительных запросов, ознакомьтесь с документацией здесь.

Затем мы можем создать dataTask для этого объекта сеанса с обработчиком завершения. Обработчик завершения по сути просто описывает, что делать, как только мы получим ответ на запрос.

Как только мы получим данные, нам просто нужно декодировать их в ранее созданный объект Joke и отправить метод завершения в наш основной поток. Код для этого выглядит следующим образом:

import Foundation

class JokesApi {
    let jokeUrl = URL(string: "https://v2.jokeapi.dev/joke/Programming")!
    
    func getRandomJoke(completion:@escaping (Joke) -> ()) {
        URLSession.shared.dataTask(with: jokeUrl) { data,_,_  in
            let joke = try! JSONDecoder().decode(Joke.self, from: data!)
            
            DispatchQueue.main.async {
                completion(joke)
            }
        }
        .resume()
    }
}

Теперь, когда у нас есть функция получения шутки, нам просто нужно отобразить ее в представлении контента.

Обновление пользовательского интерфейса

В нашем пользовательском интерфейсе нам нужна переменная состояния для хранения нашей шутки, и нам нужен способ вызова функции getRandomJoke(). Мы можем сделать это либо нажатием кнопки, либо просто при появлении представления.

struct ContentView: View {
    @State var joke: Joke?
    
    var body: some View {
        VStack {
            Text("Random Joke")
                .font(.title)
                .fontWeight(.black)
                .foregroundColor(Color.gray)
                .padding(.bottom, 8)
            Text(joke?.joke ?? "Loading joke...")
                .padding(.horizontal, 8)
        }.onAppear() {
            JokesApi().getRandomJoke { (joke) in
                self.joke = joke
            }
        }
    }
}

В предварительном просмотре теперь должна отображаться случайная шутка.

Улучшения

Код работает, но вы, возможно, заметили, что мы не очень корректно обрабатываем ошибки. В качестве улучшения вам следует добавить лучшую обработку ошибок в функции getRandomJoke, например, если возвращенные данные не могут быть проанализированы.

Спасибо! ❤️

Спасибо за чтение, и я надеюсь, что это было полезно! 👍

В настоящее время я пытаюсь разблокировать новую функцию в Medium, которая доступна только для участников, у которых более 100 подписчиков. Если вы хотите поддержать меня, я был бы признателен, если бы вы подписались на меня и помогли мне достичь этой цели. 😊