356
Сетевые
операции в приложении
происходящее здесь так же, как объяснили опытному коллеге, который может
просмотреть код или протестировать его.
Здесь определяется класс, наследующий
Ac ti vi ty
, а значит, он представляет
отдельный экран с информацией. Наш класс ожидает получить строку с поис
-
ковым запросом из
SearchView
в
BrowseContentAc ti vi ty
.
Согласно определению макета, первоначально
Ac ti vi ty
отображает вращаю
-
щийся индикатор
ProgressBar
в центре экрана, чтобы показать, что приложение
выполняет некоторую работу в фоновом режиме.
RecyclerView
не отображается,
содержимое не будет благополучно загружено.
В процессе создания мы берем строку с поисковым запросом и добавляем
ее в конец известного URL веб-службы. Для отправки
запроса на сервер мы
используем стандартные библиотечные классы, такие как
URL
и
HttpConnection
,
а полученную в ответ последовательность байтов преобразуем в строку.
Чтобы не блокировать пользовательский интерфейс, сетевой запрос выпол
-
няется в фоновом потоке – мы уже видели, что для получения даже простого
ответа иногда требуется несколько секунд (или больше), и было бы нехорошо,
если бы пользовательский интерфейс зависал на это время.
Мы знаем, что ответ возвращается в формате JSON, поэтому используем
библиотеку Gson для его парсинга и преобразования в коллекцию объектов
Location
. Если все прошло успешно, мы скрываем
ProgressBar
и отображаем
Re
cyclerView
, после чего передаем экземпляры
Location
в адаптер и подключаем
его. Если сетевой запрос или операция десериализации потерпели неудачу, мы
просто выводим ошибку в журнал – в готовом приложении, вероятно, следо
-
вало бы вывести сообщение в пользовательском интерфейсе или просто по
-
вторить операцию.
Теперь, после заполнения
RecyclerView
и адаптера, мы должны увидеть спи
-
сок всех объектов
Location
, полученных в ответ на поисковый запрос, в форме
простых строк, содержащих адреса библиотек.
Даже не самый лучший код, написанный на скорую руку, может выглядеть
довольно простым, если знать, как используются имеющиеся инструменты.
А теперь посмотрим, как то же самое сделать в iOS.
iOS
В Xcode добавьте в проект новый файл с исходным кодом на Swift и с именем
LocationsController
.
Как вы помните, главная цель нашего пользовательского
интерфейса – получить список местоположений библиотек для данной стра
-
ны. Созданная нами служба имеет конечную точку, выполняющую именно
эту функцию. Если вы откроете страницу
http://
localhost:3000/loca tion?c
ount
ry=<название_страны>
, то увидите список библиотек, находящихся в этой стране.
Итак, у нас есть параметр
country
, в котором можно передать название страны.
Контроллер
LocationsController
можно
представить как класс, имеющий
единственный метод, выбирающий библиотеки для данной страны. Выразить
этот метод можно следующим образом:
func fetchLocations(for country: String) > [Location] {
}
Вызов службы
357
Нам также нужно определить структуру
Location
. Мы знаем, что будем полу
-
чать ответы в формате JSON, поэтому используем структуру и протокол
Codable
,
подобно тому, как мы это сделали в отношении структуры
Book
. Используя файл
location.json
в качестве руководства, получаем
следующий объект
Location
:
struct Location: Codable {
let streetAddress:
String
let city: String
let country: String
let emoji: String
let hours: String
private enum CodingKeys: String, CodingKey {
case streetAddress = "the_address"
case city
case country
case emoji
case hours
}
}
Обратите внимание на свойство
streetAddress
. В
документе JSON это свойство
называется
the_address
. Поскольку имена свойств в структуре
Location
и в файле
JSON не совпадают, было добавлено приватное перечисление
CodingKeys
, которое
обеспечивает правильное сопоставление имен свойств в JSON и в структуре
Loca
tion
. К сожалению, даже если не совпадает имя всего одного свойства, необходимо
перечислить имена всех свойств.
При этом
JSONDecoder
может помочь в некоторых случаях, таких как преоб
-
разование имен в нотации с символами подчеркивания, используемых в JSON,
в имена свойств в верблюжьей нотации структуры, например:
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
...
Если теперь использовать только что объявленный метод
fetchLocations(for:)
,
он прекрасно будет работать с быстрым соединением. Но он действует син
-
хронно, что может стать проблемой при получении большого объема данных
или с низкоскоростным соединением. То есть если вызвать его из основного
потока, пользователь будет вынужден сидеть и ждать завершения процесса,
прежде чем что-то произойдет; наше приложение будет выглядеть зависшим.
Это не самое лучшее решение.
К счастью, эту проблему легко исправить с помощью замыканий. Типичный
шаблон выполнения сетевых операций в Swift заключается в использовании
обработчика, который выполняется по завершении операции. Давайте изме
-
ним сигнатуру метода так, чтобы он передавал возвращаемый массив
[Loca
tion]
в обработчик события завершения операции. Также добавим обработчик
ошибок на случай, если операция завершится с ошибкой. Вот как теперь дол
-
жен выглядеть метод:
func fetchLocations(for country: String,
completionHandler: @escaping ([Location]) -> (),
358
Сетевые операции в приложении
errorHandler: @escaping (Error?) > ()) {
}
А после добавления структуры
Location
содержимое файла должно выглядеть
так:
import
Foundation
class LocationsController {
func fetchLocations(for country: String,
completionHandler: @escaping ([Location]) -> (),
errorHandler: @escaping (Error?) >
()) {
}
}
struct Location: Codable {
let streetAddress: String
let city: String
let country: String
let emoji: String
let hours: String
private enum CodingKeys: String, CodingKey {
case streetAddress = "street_address"
case city
case country
case emoji
case hours
}
}
Do'stlaringiz bilan baham: