Сеть это сложно. Существуют различные движущиеся части, и для его работы необходимо учитывать множество факторов. К счастью, с течением времени появилось несколько библиотек с открытым исходным кодом, которые облегчают работу в сети. AFNetworking, созданный и поддерживаемый людьми Gowalla, является одной из таких библиотек. Этот учебник познакомит вас с платформой AFNetworking, а также покажет, как запрашивать API iTunes Store!
В этом уроке я познакомлю вас с AFNetworking и покажу, что может предложить эта библиотека. Потратив несколько минут с этой библиотекой, вы заметите, что она была разработана с учетом удобства использования. Это не только ускорит вашу разработку, но также позаботится о многих кропотливых сетевых задачах. Мы создадим простое приложение, которое запрашивает в iTunes Store фильмы, соответствующие поисковому запросу «Гарри». Результаты нашего запроса будут отображены в виде таблицы.
Краткое описание проекта
Приложение, которое мы собираемся создать, запрашивает API поиска iTunes Store. В частности, мы ищем в iTunes Store фильмы, соответствующие поисковому запросу «Гарри». Если наш запрос выполнен успешно, мы обрабатываем результаты и отображаем их в виде таблицы. Каждая строка представляет фильм с названием, режиссером и миниатюрой, показывающей обложку фильма. Готов? Давайте начнем.
Настройка проекта
Прежде чем мы запачкаем руки с помощью AFNetworking, нам нужно создать базовую основу. Это означает настройку нашего проекта, создание табличного представления и добавление представления индикатора активности. Мы покажем индикатор активности, когда наш запрос обрабатывается iTunes Store. Это даст пользователю тот ценный дополнительный бит обратной связи, который часто упускается из виду.
Создайте новый проект в XCode, выбрав шаблон приложения Single View из списка шаблонов. Назовите свое приложение NetworkingIsFun , введите идентификатор компании, установите «iPhone» для семейства устройств и снимите флажок «Использовать раскадровки». Вы можете оставить остальные нетронутыми, но убедитесь, что установлен флажок Использовать автоматический подсчет ссылок . Сообщите Xcode, где вы хотите сохранить свой проект, и нажмите «Сохранить».
Добавление представлений таблицы и индикатора активности
Несмотря на то, что Interface Builder великолепен, я часто создаю свои интерфейсы программно, и именно это мы и сделаем в этом уроке. Это позволит нам просто сосредоточиться на коде, не отвлекаясь на Interface Builder. Откройте ViewController.h и создайте три переменных экземпляра (ivars), а также свойства для этих ivars. Поскольку мы собираемся работать с табличным представлением, не забудьте привести свой контроллер представления в соответствие с протоколами UITableViewDataSource и UITableViewDelegate . Заголовочный файл вашего контроллера представления должен выглядеть примерно так, как показано ниже:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> {
UITableView *_tableView;
UIActivityIndicatorView *_activityIndicatorView;
NSArray *_movies;
}
@property (nonatomic, retain) UITableView *tableView;
@property (nonatomic, retain) UIActivityIndicatorView *activityIndicatorView;
@property (nonatomic, retain) NSArray *movies;
@end
|
Если вас смущает использование подчеркивания, я рекомендую вам прочитать об этом здесь . Не стесняйтесь опускать подчеркивания, если вы думаете, что это выглядит некрасиво или заставляет вас чувствовать себя некомфортно. Помимо подчеркивания, не должно быть никаких сюрпризов. Мы объявляем наши UITableView и UIActivityIndicatorView, а также NSArray, который мы будем использовать для хранения результатов, которые мы получаем из нашего поискового запроса. Готов? Давайте перейдем к файлу реализации нашего контроллера представления.
Поскольку мы объявили три свойства в нашем заголовочном файле, нам нужно синтезировать их методы доступа в ViewController.m . Опять же, если подчеркивание сбивает вас с толку, вы можете оставить их.
1
|
@synthesize tableView = _tableView, activityIndicatorView = _activityIndicatorView, movies = _movies;
|
В нашем методе viewDidLoad мы настраиваем представления таблицы и индикатора активности. Код ниже должен быть понятен по большей части. Если вы никогда не настраивали табличное представление без использования Interface Builder, вы можете увидеть несколько строк, которые вам незнакомы. Вместо того, чтобы связывать табличное представление в Интерфейсном Разработчике, мы заботимся об этом в методе viewDidLoad . После вызова метода viewDidLoad суперкласса мы инициализируем наше табличное представление с помощью фрейма и стиля и устанавливаем наш контроллер представления в качестве источника данных и делегата нашего табличного представления. Когда наше приложение запускается, мы скрываем наше табличное представление, так как нам нечего показывать, если наш запрос не дал результатов. Перед добавлением табличного представления в качестве подпредставления к представлению нашего контроллера представления, мы устанавливаем его маску авторазмера. Маска авторазмера определяет, как будет изменяться размер табличного представления, если родительское представление — представление контроллера представления, к которому мы добавляем табличное представление — изменяется в размере. Это происходит, например, когда устройство поворачивается. Смущенный? Не беспокойся об этом. Это не важно для этого приложения.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
— (void)viewDidLoad {
[super viewDidLoad];
// Setting Up Table View
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.bounds.size.width, self.view.bounds.size.height) style:UITableViewStylePlain];
self.tableView.dataSource = self;
self.tableView.delegate = self;
self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth |
self.tableView.hidden = YES;
[self.view addSubview:self.tableView];
// Setting Up Activity Indicator View
self.activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
self.activityIndicatorView.hidesWhenStopped = YES;
self.activityIndicatorView.center = self.view.center;
[self.view addSubview:self.activityIndicatorView];
[self.activityIndicatorView startAnimating];
// Initializing Data Source
self.movies = [[NSArray alloc] init];
}
|
Настроить вид индикатора активности так же просто. Мы инициализируем представление индикатора активности с предопределенным стилем, устанавливаем для его свойства hidesWhenStopped значение YES и помещаем его в центр родительского представления. После добавления его в представление контроллера представления мы начинаем анимировать индикатор активности. Индикатор активности будет отображаться автоматически, поскольку мы установили для его свойства hidesWhenStopped значение YES.
В конце нашего метода viewDidLoad мы инициализируем массив movies . Мы будем использовать его позже, чтобы сохранить результаты нашего поискового запроса.
Протоколы табличного представления
В этом руководстве мы реализуем только два метода протокола источника данных табличного представления. Оба эти метода являются обязательными. Это минимальная реализация, необходимая для запуска и запуска нашего табличного представления. Даже если мы установили наш контроллер представления в качестве делегата табличного представления, мы не будем использовать ни один из методов делегата в нашем приложении. Если вы уже использовали табличное представление, вы не найдете сюрпризов в реализациях методов, показанных ниже.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
— (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (self.movies && self.movies.count) {
return self.movies.count;
} else {
return 0;
}
}
— (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellID = @»Cell Identifier»;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID];
}
return cell;
}
|
В tableView: numberOfRowsInSection: нам нужно вернуть количество строк в каждом разделе табличного представления. В нашем примере табличное представление содержит только один раздел (по умолчанию), что делает все немного проще. Сначала мы проверяем, не является ли наша переменная movies значение nil, и проверяем, что она содержит элементы. Если оба эти требования выполнены, мы возвращаем количество элементов в массиве movies, если нет, мы возвращаем ноль.
Наш tableView: cellForRowAtIndexPath: метод также является базовым. Мы начинаем с того, что спрашиваем наше табличное представление, есть ли ячейка, которую мы можем использовать повторно. Если это не так, мы создаем новую ячейку со стилем и повторно используем идентификатор. Мы заканчиваем нашу реализацию, возвращая нашу ячейку. Это будет делать сейчас. Теперь вы можете создавать и запускать ваше приложение. Если вы правильно выполнили все шаги, вы должны увидеть, что индикатор активности вращается как сумасшедший, а представление таблицы должно быть скрыто.
Добавление AFNetworking в Mix
Добавить AFNetworking в ваш проект очень просто. Начните с загрузки библиотеки с GitHub и распакуйте архив. Архив содержит папку с именем AFNetworking , в которой содержатся исходные файлы, которые мы должны включить в наш проект. Перетащите всю эту папку в свой проект XCode и убедитесь, что вы отметили Копирование элементов в папку целевой группы (если необходимо) и также добавили исходные файлы в свою цель.
После добавления библиотеки AFNetworking в проект создайте и запустите приложение и наблюдайте, как все разваливается. Что случилось с нашим проектом? Почему мы получаем все эти предупреждения и ошибки? Когда мы настроили наш проект Xcode, мы включили автоматический подсчет ссылок (ARC) . На момент написания, библиотека AFNetworking не использовала ARC. Не волнуйтесь, мы можем использовать эту аккуратную библиотеку с минимальными усилиями. Все, что нам нужно сделать, это сообщить компилятору, что все исходные файлы библиотеки AFNetworking не используют ARC. Вот и все.
как нам это сделать? Выберите ваш проект в Навигаторе проектов и выберите цель. Перейдите на вкладку « Фазы сборки » в верхней панели навигации и откройте панель « Скомпилировать источники» . В этой таблице показаны все исходные файлы, которые компилятор будет компилировать во время компиляции. В левом столбце показаны имена файлов, а в правом столбце — флаги, о которых должен знать компилятор.
Вы можете видеть эти флаги как инструкции или сообщения для компилятора. Все, что вам нужно сделать, это добавить флаг компилятора в каждый исходный файл библиотеки AFNetworking. Для этого выберите исходный файл из списка и дважды щелкните ячейку в правом столбце. Появится небольшое окно, в которое вы можете добавить один или несколько флагов компилятора. В нашем случае просто введите -fno-objc-arc и нажмите Готово . Этот флаг сообщает компилятору, что исходный файл не использует ARC. Убедитесь, что вы добавили этот флаг ко всем десяти исходным файлам библиотеки AFNetworking.
Опрос API поиска в iTunes Store
AFNetworking — это библиотека, которая может многое сделать для вас, но сегодня мы собираемся использовать только две изящные функции. Прежде чем мы сможем начать использовать классы AFNetworking, нам нужно добавить следующий оператор импорта чуть ниже первого оператора импорта в файле реализации вашего контроллера представления.
1
|
#import «AFNetworking.h»
|
Это утверждение импорта даст нам доступ ко всем классам AFNetworking. Вернитесь к методу viewDidLoad нашего контроллера представления и добавьте следующий фрагмент сразу после инициализации массива movie .
01
02
03
04
05
06
07
08
09
10
11
|
NSURL *url = [[NSURL alloc] initWithString:@»http://itunes.apple.com/search?term=harry&country=us&entity=movie»];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(@»%@», JSON);
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(@»Request Failed with Error: %@, %@», error, error.userInfo);
}];
[operation start];
|
Позвольте мне объяснить, что происходит. В первой строке мы создаем NSURL для нашего запроса. Строка, которую мы используем при инициализации, соответствует формату, который ожидает API поиска iTunes Store. Синтаксис API поиска довольно прост: термин, который мы ищем, «Гарри», мы ограничиваем наш поиск в iTunes Store в США и ищем только фильмы. Легко, правда?
Затем мы инициализируем NSURLRequest и передаем NSURL, который мы только что создали. Затем вступает AFNetworking. AFNetworking содержит несколько очень специализированных классов, которые делают нашу работу очень легкой. Здесь мы используем AFJSONRequestOperation . Этот класс предназначен для извлечения и анализа данных JSON в фоновом режиме. Как следует из названия, этот класс является подклассом NSOperation или, если быть более точным, один из суперклассов этого класса наследуется от NSOperation. Класс позволяет вам получить запрошенные данные, а также анализирует ответ JSON. Это означает, что нам не нужно иметь дело с необработанным JSON. Возвращаемые данные готовы к использованию в вашем приложении. AFNetworking использует встроенный анализатор JSON в iOS 5 и использует собственный анализатор JSON для более старых версий iOS.
Использовать AFJSONRequestOperation легко, поскольку он имеет только один метод класса. Этот метод класса принимает три аргумента: (1) NSURLRequest, (2) блок успеха, выполняемый при успешном выполнении запроса, и (3) блок отказа, исполняемый при сбое запроса. Если блоки являются новыми для вас или вам неудобно их использовать, то я рекомендую прочитать учебник Коллина Руффенаха о блоках и перечислении на Mobiletuts +. Успешный блок принимает три аргумента: (1) наш NSURLRequest, (2) NSHTTPURLResponse нашего запроса и (3) проанализированный JSON-объект. Блок отказов практически идентичен. Единственное отличие состоит в том, что он принимает дополнительный аргумент, NSError, который содержит больше информации о том, что пошло не так в случае сбоя нашего запроса.
В целях тестирования мы регистрируем проанализированный объект JSON, чтобы увидеть, что API-интерфейс поиска в iTunes Store отправляет нам обратно. Кроме того, мы также регистрируем ошибку в блоке сбоя в случае сбоя нашего запроса. Прежде чем снова собирать и запускать наше приложение, нам нужно запустить операцию, вызвав start нашего объекта операции. Создайте и запустите ваше приложение и посмотрите на вывод в консоли.
Если все прошло хорошо, вы увидите результаты нашего запроса, записанные в консоли. Анализируемый объект JSON представляет собой словарь с двумя ключами: (1) resultCount , который содержит количество возвращаемых результатов и (2) фактические результаты в виде массива словарей. Мы не только зарегистрировали ответ на консоли, чтобы увидеть, был ли наш запрос успешным, мы теперь можем видеть ключи для каждого элемента в массиве результатов. Нам понадобятся эти ключи для отображения некоторой информации в нашем табличном представлении.
Заполнение табличного представления
Теперь мы готовы показать результаты в нашем табличном представлении. Замените оператор журнала в блоке успеха на фрагмент, показанный ниже. Мы начнем с присвоения массива результатов объекта ответа массиву movies. Осталось только скрыть индикатор активности, остановив его, отобразив табличное представление и перезагрузив табличное представление с новыми данными, сохраненными в массиве movies.
1
2
3
4
|
self.movies = [JSON objectForKey:@»results»];
[self.activityIndicatorView stopAnimating];
[self.tableView setHidden:NO];
[self.tableView reloadData];
|
Затем мы изменяем tableView: cellForRowAtIndexPath: метод. Настройте свою реализацию так, чтобы она соответствовала приведенной ниже. После получения ссылки на ячейку, мы запрашиваем массив фильмов для правильного элемента и обновляем метки ячейки заголовком и режиссером фильма. Создайте и запустите ваше приложение.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
— (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellID = @»Cell Identifier»;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID];
}
NSDictionary *movie = [self.movies objectAtIndex:indexPath.row];
cell.textLabel.text = [movie objectForKey:@»trackName»];
cell.detailTextLabel.text = [movie objectForKey:@»artistName»];
return cell;
}
|
Возможно, вы заметили, что нет миниатюр, которые можно увидеть. Давайте исправим это, добавив три дополнительные строки в наш tableView: cellForRowAtIndexPath: метод. Создайте и запустите ваше приложение.
1
2
3
|
NSURL *url = [[NSURL alloc] initWithString:[movie objectForKey:@»artworkUrl100″]];
NSData *data = [NSData dataWithContentsOfURL:url];
cell.imageView.image = [[UIImage alloc] initWithData:data];
|
Вы заметили, что наше табличное представление не прокручивается плавно. Это почему? Как я уже упоминал в предыдущем руководстве , вы всегда должны следить за тем, чтобы основной поток вашего приложения оставался отзывчивым. В текущей реализации нашего метода tableView: cellForRowAtIndexPath: мы загружаем миниатюры в основном потоке. Это означает, что пользовательский интерфейс не может быть обновлен, пока не будет завершен запрос эскиза. Наши миниатюры крошечные, поэтому запросы не занимают слишком много времени, но представьте, что вы используете тот же подход для больших активов. Пользовательский опыт был бы ужасен. Даже для нашего простого приложения пользовательский опыт неприемлем. Однако мы можем исправить это с помощью очень полезной функции библиотеки AFNetworking.
AF Networking на помощь
Создатели AFNetworking также увидели необходимость загрузки ресурсов в фоновом режиме. Поэтому они создали категорию для UIImageView. Эта категория позволяет загружать изображения в фоновом режиме только с двумя строками кода. Эта категория — настоящий спасатель жизни. Посмотрите на фрагмент ниже.
1
2
|
NSURL *url = [[NSURL alloc] initWithString:[movie objectForKey:@»artworkUrl100″]];
[cell.imageView setImageWithURL:url placeholderImage:[UIImage imageNamed:@»placeholder»]];
|
Первая строка кода остается прежней. Во второй строке мы сообщаем представлению изображения, где находится миниатюра, передавая NSURL, и передаем изображение-заполнитель, которое отображается, пока наш запрос не вернул ответ. Как это круто? Все, что нам нужно сделать, это добавить изображение-заполнитель в наш проект. Это может быть любое изображение, которое вы хотите, но вы можете найти изображение, которое я использовал в качестве заполнителя, в файле загрузки, прилагаемом к этому учебному пособию. После того, как вы добавили изображение-заполнитель, соберите и запустите ваше приложение и убедитесь, насколько плавно прокручивается табличное представление!
Вывод
Обратите внимание, что наше приложение очень простое в своем исполнении, так как оно не выполняет никакого кэширования, и мы можем искать в iTunes Store только термин «гарри» в разделе фильмов. Однако, приложив совсем немного усилий, вы можете создать аккуратное приложение для более быстрого поиска в iTunes Store.
Я надеюсь, что этот урок убедил вас, что библиотека AFNetworking — отличный инструмент для вашего арсенала. Он может сделать намного больше, чем то, что я показал вам в этом посте, но основная цель этого урока — научить вас работать с AFNetworking и быть готовым использовать его в реальном сценарии.