Что такое JSONModel?
Наши устройства iOS подключены к Интернету большую часть времени, и, естественно, большинство приложений на наших устройствах подключаются к удаленному серверу, чтобы время от времени получать тот или иной кусок данных.
Некоторые приложения потребляют немного информации, получая последние заголовки каждый час или около того. Другие приложения активно взаимодействуют с бэкэнд-сервисом, в то время как пользователь просматривает их социальные сети, читает через сообщения и загружает фотографии.
Дни, когда каждый веб-сервис говорил на XML, давно прошли. В настоящее время большинство мобильных приложений взаимодействуют с веб-сервисами с использованием JSON . Если вы планируете создать мобильное приложение, которое взаимодействует с удаленным бэкэндом, есть вероятность, что вам нужно будет иметь возможность отправлять и получать JSON.
JSONModel — это библиотека с открытым исходным кодом, написанная на Objective-C, которая помогает вам извлекать JSON с сервера, анализировать его и инициализировать классы модели с данными. Он также проверяет данные JSON, каскады через вложенные модели и многое другое.
«Но ждать!» Вы можете подумать: «Я уже написал приложение для iPhone, которое получает JSON и показывает его на экране. Это было довольно легко!»
Ну, это отчасти правда. NSJSONSerialization
была доступна с iOS 5, поэтому действительно довольно легко преобразовать JSON-ответ в объект NSDictionary
. Это хорошо работает для простых приложений, но поверьте мне, когда я говорю, что это не очень хорошая идея для сложного приложения со сложной моделью данных. Давайте посмотрим, как JSONModel может сохранить ваш бекон.
Обратите внимание, что я являюсь автором JSONModel, разрабатывающим и поддерживающим библиотеку с помощью участников GitHub . Я явно предвзят, но это хорошая новость для вас, так как вы сможете учиться у человека, который создал библиотеку.
Основные характеристики
В этом разделе я кратко остановлюсь и рассмотрю основные функции библиотеки. Если вам не терпится погрузиться в код, перейдите к следующему разделу, «Привет, Чак, приложение» .
Автоматическое сопоставление JSON с модельными классами
Когда вы смотрите на данные JSON, которые заполняют ваш объект модели, вы часто чувствуете, что хотите совпасть с именами ключей, используемых в данных JSON. В итоге вы пишете код, который выглядит следующим образом:
1
2
3
|
self.firstName = [json objectForKey:@»firstName»];
self.familyName = [json objectForKey:@»familyName»];
self.age = [json objectForKey:@»age»];
|
С JSONModel вам не нужно писать этот тип шаблонного кода. JSONModel автоматически сопоставляет JSON со свойствами класса модели.
Проверка входных данных
JSONModel автоматически проверяет свойства вашего класса модели и обеспечивает соответствие JSON, который используется для инициализации объекта модели, определению класса модели. В случае несоответствия объект модели не будет инициализирован.
Кроме того, модель проверяет, соответствуют ли данные JSON типам, определенным классом модели. Например, если вместо строки вы получаете массив, данные JSON считаются недействительными.
Преобразование данных
Из-за простой спецификации JSON его легко использовать, но он также удаляет много метаданных, когда используется для передачи данных из серверной части в клиент и наоборот. Объект JSON может содержать только строки, числа, массивы и объекты.
В вашем классе модели Objective C вы обычно имеете свойства различных типов, не ограничиваясь строками и числами, которые являются единственными типами данных, поддерживаемыми JSON. Например, у вас часто есть URL-адреса в объекте JSON. Преобразовать строку в объекте JSON в объект NSURL
, но раздражает то, что вам нужно сделать это самостоятельно.
JSONModel позволяет вам определить преобразования для типов данных один раз и использовать их в своих моделях. Например, если в ответе JSON указана дата в виде временной метки в виде целого числа, вам нужно только указать JSONModel, как преобразовать целое число в объект NSDate
один раз. Вы узнаете больше о преобразованиях данных во второй части этой серии.
Вложенные модели
Чаще всего ответ JSON имеет сложную структуру. Например, объект может содержать один или несколько других объектов. Посмотрите на следующий объект JSON.
1
2
3
4
5
6
7
|
{
«id»: 10,
«more»: {
«text»: «ABC»,
«count»: 20
}
}
|
JSONModel позволяет вам также вкладывать классы моделей. Не имеет значения, содержит ли ваша модель другую модель или массив объектов модели, JSONModel проверяет классы модели и автоматически инициализирует объекты правильного типа. Мы подробнее рассмотрим вложенные модели чуть позже.
Этого достаточно для теории. Давайте узнаем, как использовать библиотеку JSONModel, создав простое приложение-пример.
Приложение Hello Chuck
Теперь, когда у вас есть базовое представление о том, что делает JSONModel, вы разработаете простое приложение, которое извлекает JSON-ленту шуток Чака Норриса и показывает их пользователю один за другим. Когда вы закончите, приложение будет выглядеть примерно так:
Шаг 1: Настройка проекта
Запустите Xcode 5, создайте новый проект, выбрав « Создать»> «Проект …» в меню « Файл» , и выберите шаблон приложения « Один вид» из списка шаблонов приложений iOS .
Назовите проект HelloChuck , сообщите Xcode, где вы хотите его сохранить, и нажмите « Создать» . Нет необходимости ставить проект под контроль версий.
Затем загрузите последнюю версию библиотеки JSONModel с GitHub , разархивируйте архив и получите пик внутри.
Архив содержит демонстрационные приложения для iOS и OSX, модульные тесты и многое другое. Вас интересует только папка с именем JSONModel . Перетащите его в свой проект Xcode. Установка еще проще, если вы используете CocoaPods .
Шаг 2. Создание классов моделей
Канал JSON, который вы собираетесь использовать, довольно прост. Он содержит массив анекдотов, каждый из которых имеет идентификатор, сам анекдот и, необязательно, массив тегов.
1
2
3
4
5
6
7
8
|
{
«id»: 7,
«text»: «There used to be a street named after Chuck Norris but it was changed because nobody crosses Chuck Norris and lives»,
«tags»: [
{ «id»:1, «tag»:»lethal» },
{ «id»:2, «tag»:»new» }
]
}
|
Давайте начнем с создания классов моделей, соответствующих данным JSON. Создайте новый класс JokeModel
и сделайте его наследником от JSONModel
. Добавьте свойства id
и text
для соответствия ключам в данных JSON следующим образом:
1
2
3
4
5
6
|
@interface JokeModel : JSONModel
@property (assign, nonatomic) int id;
@property (strong, nonatomic) NSString* text;
@end
|
Библиотека JSONModel автоматически преобразует числа в соответствии с типом свойства.
Вам также необходимо создать класс для объектов тега в данных JSON. Создайте новый класс TagModel
и сделайте так, чтобы он наследовал JSONModel
. Объявите два свойства id
и tag
типа NSString
. Класс TagModel
должен выглядеть следующим образом:
1
2
3
4
5
6
|
@interface TagModel : JSONModel
@property (strong, nonatomic) NSString* id;
@property (strong, nonatomic) NSString* tag;
@end
|
Обратите внимание, что вы установили тип id
в NSString
. JSONModel прекрасно знает, как преобразовать числа в строки, он будет обрабатывать преобразование для вас. Идея состоит в том, что вам нужно сосредоточиться только на данных, которые вам нужны в вашем приложении, не беспокоясь о том, как выглядят данные JSON.
Хотя класс TagModel
готов к использованию, вам нужен способ сообщить классу JokeModel
что ключевые tags
содержат список экземпляров TagModel
. Это очень легко сделать с помощью JSONModel. Добавьте новый пустой протокол в TagModel.h и назовите его TagModel
:
1
2
|
@protocol TagModel
@end
|
Откройте JokeModel.h и импортируйте файл TagModel
класса TagModel
:
1
|
#import «TagModel.h»
|
Здесь приходит волшебство. JokeModel
новое свойство для JokeModel
как показано ниже. Свойство tags
имеет тип NSArray
и соответствует двум протоколам.
1
|
@property (strong, nonatomic) NSArray<TagModel, Optional>* tags;
|
-
TagModel
— это протокол, который вы объявили минуту назад. Он сообщаетJokeModel
что массив тегов должен содержать экземпляры классаTagModel
. - Придерживаясь
Optional
протокола, классJokeModel
знает, что данные JSON не всегда будут содержать список тегов.
Это хороший момент, чтобы подчеркнуть, что каждое свойство в вашем классе модели по умолчанию требуется . Если id
или text
отсутствуют в данных JSON, инициализация объекта JokeModel
завершится неудачно. Однако, если для какой-то шутки отсутствуют tags
, JSONModel не будет жаловаться на это.
Шаг 3: Просмотр настроек контроллера
Сначала вам нужно внести пару изменений в класс ViewController
. Откройте ViewController.m и, под существующим оператором import вверху, импортируйте класс JokeModel
:
1
|
#import «JokeModel.h»
|
Вам нужно добавить два свойства в класс ViewController:
-
label
для отображения текста шутки на экране -
jokes
для хранения массива шуток
1
2
3
4
5
6
|
@interface ViewController ()
@property (strong, nonatomic) UILabel* label;
@property (strong, nonatomic) NSArray* jokes;
@end
|
Вам также необходимо настроить метку так, чтобы она была готова всякий раз, когда вы извлекаете данные JSON и у вас есть шутка, готовая для отображения. Обновите метод viewDidLoad
как показано ниже.
01
02
03
04
05
06
07
08
09
10
11
12
|
— (void)viewDidLoad {
[super viewDidLoad];
self.label = [[UILabel alloc] initWithFrame:self.view.bounds];
self.label.numberOfLines = 0;
self.label.textAlignment = NSTextAlignmentCenter;
self.label.alpha = 0;
[self.view addSubview: self.label];
[self fetchJokes];
}
|
Вы создаете экземпляр UILabel
размером с экран устройства и устанавливаете его alpha
свойство UILabel
0
. Ярлык скрыт, пока первая шутка не будет готова для отображения.
В последней строке viewDidLoad
вы вызываете fetchJokes
, в котором приложение извлекает удаленные данные JSON и сохраняет их содержимое в свойстве jokes
контроллера представления. Вы будете реализовывать fetchJokes
в мгновение fetchJokes
.
Шаг 4. Извлечение JSON и создание объектов модели
В этом примере вы будете использовать класс NSURLSession
для выборки удаленных данных JSON. Вы создаете URL для запроса, инициализируете задачу с данными и отправляете ее в путь.
1
2
3
4
5
6
7
|
— (void)fetchJokes {
NSURL* jokesUrl = [NSURL URLWithString:@»https://s3.amazonaws.com/com.tuts.mobile/jokes.json»];
[[[NSURLSession sharedSession] dataTaskWithURL:jokesUrl completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
//handle data here
}] resume];
}
|
dataTaskWithURL:completionHandler:
создает для экземпляра NSURLSessionDataTask
URL-адрес, который ему передается. NSURLSession
resume
для задачи с данными, вы NSURLSession
экземпляру NSURLSession
добавить задачу с данными в свою очередь.
Затем вам нужно добавить код для инициализации экземпляров JokeModel
. Заменить //handle data here
:
1
|
self.jokes = [JokeModel arrayOfModelsFromData:data error:nil];
|
arrayOfModelsFromData:error:
берет объект NSData
из ответа JSON и возвращает массив моделей. Но что происходит под капотом?
-
[JokeModel arrayOfModelsFromData:error:]
берет данные JSON и превращает их в массив объектов JSON. - Затем
JokeModel
зацикливается на этих объектах и создает экземплярыJokeModel
из каждого объекта JSON. - Каждый экземпляр
JokeModel
проверяет полученные данные JSON и инициализирует их свойства правильными значениями. - Если экземпляр
JokeModel
находит содержимое в ключеtags
данных, то он создает массив экземпляровTagModel
из значения, связанного с ключомtags
.
Если вам нужно создать только один экземпляр модели, тогда initWithData:
и initWithString:
— это методы, которые вам нужно использовать. Мы подробнее рассмотрим эти методы в следующем уроке.
После инициализации массива анекдотов вы можете отобразить первый анекдот пользователю, используя следующий фрагмент кода.
1
2
3
|
dispatch_async(dispatch_get_main_queue(), ^{
[self showNextJoke];
});
|
Шаг 5: Отображение анекдотов
01
02
03
04
05
06
07
08
09
10
11
12
|
— (void)showNextJoke {
JokeModel* model = self.jokes[arc4random() % self.jokes.count];
NSString* tags = model.tags?[model.tags componentsJoinedByString:@»,»]:@»no tags»;
self.label.text = [NSString stringWithFormat:@»%i. %@\n\n%@», model.id, model.text, tags];
[UIView animateWithDuration:1.0 animations:^{
self.label.alpha = 1.0;
} completion:^(BOOL finished) {
[self performSelector:@selector(hideJoke) withObject:nil afterDelay:5.0];
}];
}
|
Сначала вы извлекаете случайную шутку из массива jokes
и сохраняете ее в model
. Если в шутке есть теги, вы сохраняете их в виде списка через запятую в переменной с именем tags
. Если в шутке нет никаких тегов, вы устанавливаете tags
@"no tags"
.
Вы обновляете метку для отображения id
, text
и tags
текущей шутки и используете анимацию исчезновения, чтобы показать шутку пользователю.
Когда анимация завершается, вы ждете пять секунд, прежде чем hideJoke
, который скрывает шутку с другой анимацией затухания. Когда анимация завершится, вы вызываете showNextJoke
еще раз.
1
2
3
4
5
6
7
|
— (void)hideJoke {
[UIView animateWithDuration:1.0 animations:^{
self.label.alpha = 0.0;
} completion:^(BOOL finished) {
[self showNextJoke];
}];
}
|
Это создает бесконечный цикл, постепенно исчезая выбранные шутки. Эффект довольно крутой. Попробуйте, запустив приложение.
Однако существует проблема, заключающаяся в том, что при распечатке массива тегов отображаются объекты TagModel
вместо строковых объектов. Такое поведение на самом деле является функцией библиотеки JSONModel. Он автоматически создает описание объекта, подобное тому, которое вы видели на предыдущем скриншоте. В нем перечислены свойства объекта модели и их значения, что действительно помогает при отладке.
Шаг 6: Настройка моделей
Чтобы завершить этот урок, вы напишите первую строчку кода модели. Модели, которые наследуются от JSONModel
, похожи на любой другой класс Objective-C. Это означает, что вы можете переопределить методы JSONModel
и настроить их поведение так, как вам нравится.
Откройте TagModel.m и переопределите метод описания по умолчанию:
1
2
3
|
— (NSString *)description {
return self.tag;
}
|
Теперь, когда вы вызываете componentsJoinedBySeparator:
в массиве тегов, вместо описания по умолчанию TagModel
вы просто получите тег в виде простого текста.
Попробуйте еще раз, запустив приложение. Теперь вы должны увидеть список тегов, аккуратно появляющихся под каждой шуткой.
Вывод
Теперь у вас есть общее представление о библиотеке JSONModel. Итак, вы узнали:
- Как создать простой класс модели, который наследуется от
JSONModel
- как определить обязательные и дополнительные свойства
- и как вложить модельные классы
В этом коротком руководстве я затронул только некоторые функции библиотеки JSONModel
. В следующих статьях этой серии вы узнаете больше о преобразовании данных, работе с удаленными API-интерфейсами JSON и познакомитесь с некоторыми более продвинутыми функциями JSONModel.