
Итак, в первой статье мы написали простое Single View приложение, включающее таблицу с несколькими ячейками.
На этот раз мы немного углубимся и сделаем несколько более амбициозных вещей. Мы будем обращаться к API поиска iTunes, парсить ответ, полученный в JSON и отображать результаты в Table View.
На первый взгляд может показаться, что все это довольно сложно и предстоит много работы, но на самом деле это не так. Все описанное выше является достаточно простым функционалом для iOS приложений и каждый уважающий себя iOS разработчик должен это уметь.
Нам понадобится Single View Application c добавленным Table View. Останавливаться на этом не будем, так как все это достаточно просто описано в первой статье.
Подключение интерфейса
Для начала, нам нужно получить указатель на наш Table View для того чтобы использовать его в коде приложения. Отправляемся в файл ViewController.swift и сразу в инициализации класса (class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {) добавляем следующую строчку:
@IBOutlet var appsTableView : UITableView
Это позволит нам ассоциировать Table View в Storyboard с переменной «appsTableView».
Переходим к Storyboard. Кликаем по View Controller с зажатым control и тянем курсор к Table View, тем самым связывая эти объекты. В появившейся менюшке Outlets выбираем «appsTableView».

Выполнение API запроса
Теперь, после того как мы подключили интерфейс, можно выполнять наши API запросы.
В файле ViewController.swift внутри инициализации класса ViewController создаем функцию searchItunesFor(searchTerm: String).
func searchItunesFor(searchTerm: String) { // Для The iTunes API слова в поисковом запросе должны быть разделены при помощи "+", поэтому нам необходимо произвести соответствующие замены. var itunesSearchTerm = searchTerm.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil) // Помимо этого, необходимо удалить все что не URL-friendly var escapedSearchTerm = itunesSearchTerm.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) var urlPath = "https://itunes.apple.com/search?term=\(escapedSearchTerm)&media=software" var url: NSURL = NSURL(string: urlPath) var session = NSURLSession.sharedSession() var task = session.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in println("Task completed") if(error) { println(error.localizedDescription) } var err: NSError? var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary if(err?) { println(error.localizedDescription) } var results: NSArray = jsonResult["results"] as NSArray dispatch_async(dispatch_get_main_queue(), { self.tableData = results self.appsTableView.reloadData() }) }) task.resume() }
Давайте по порядку.
Исправляем наш поисковый запрос, чтобы iTunes API получил текст вида «First+Second+Third+Words» вместо «First%20Second%20…». Для этого мы используем доступный в NSString метод stringByReplacingOccurencesOfString, который заменяет пробелы на "+".
Далее, мы очищаем полученную строку от символов, которые не поддерживаются URL.
Следующие две строчки определяют объект NSURL, который будет использоваться в качестве запроса к API.
Обратим внимание на следующие две строчки:
var session = NSURLSession.sharedSession() var task = session.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in
Первая берет стандартный объект NSURLSession, который используется для сетевых вызовов.
Вторая создает задание, которое и посылает запрос.
Наконец, строчка task.resume() начинает выполнение запроса.
Подготовка к получению ответа
Мы получили метод, который при вызове выполняет поиск в iTunes. Вставим его в конце метода viewDidLoad в нашем ViewController.
override func viewDidLoad() { super.viewDidLoad() searchItunesFor("Angry Birds") }
Теперь, для того чтобы получить ответ, необходимо добавить объект, который будет хранить результаты поиска.
Поэтому, добавим инстанс NSMutableData и массив NSArray, для хранения данных для нашей таблицы (внутри инициализации класса ViewController, например сразу после указателя @IBOutlet var appsTableView: UITableView).
var data: NSMutableData = NSMutableData() var tableData: NSArray = NSArray()
Теперь, давайте объединим функции, которые NSURLConnection будет отправлять в наш класс. Так как это делегат нашего запроса, любая информация от NSURLConnection будет возвращаться методами протокола, определенными в NSURLConnectionDataDelegate и NSURLConnectionDelegate. Поэтому, в инициализации ViewController укажем также NSURLConnectionDelegate, NSURLConnectionDataDelegate, будет что-то вроде:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, NSURLConnectionDelegate, NSURLConnectionDataDelegate {
Получение ответа
Нам предстоит добавить самый большую часть кода нашего приложения для обработки полученной информации.
func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) { self.data = NSMutableData() } func connection(connection: NSURLConnection!, didReceiveData data: NSData!) { self.data.appendData(data) } func connectionDidFinishLoading(connection: NSURLConnection!) { var err: NSError var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary if jsonResult.count>0 && jsonResult["results"].count>0 { var results: NSArray = jsonResult["results"] as NSArray self.tableData = results self.appsTableView.reloadData() } }
Когда NSURLConnection получает ответ, вызывается метод didReceiveResponse.
Тут мы просто сбрасываем наши данные, если они были, прописывая self.data = NSMutableData() для создания нового объекта.
После установки соединения, мы начинаем получать данные в методе didReceiveData. Здесь передается аргумент data: NSData, где и находится вся интересующая нас информация. Нам нужно сохранить все полученные в ответе части, поэтому мы присоединяем их к объекту self.data, созданному выше.
Наконец, после того как мы в ответе получили всю информацию, вызывается метод connectionDidFinishLoading, где мы уже можем начать использовать результат.
Мы будем использовать класс NSJSONSerialization для преобразования необработанных данных в полезную информацию в виде объектов словаря NSDictionary.
Теперь, когда мы убедились что получен какой-то ответ от iTunes, простой проверки ключа «results» будет достаточно для того чтобы удостовериться что мы получили именно то что ожидали, поэтому мы можем установить объект self.tableData равным results, а также обратиться к методу таблицы appsTableView.reloadData() для обновления ее содержимого.
Обновление интерфейса Table View UI
Для инициализации Table View в первой статье, нам необходимо было определить две функции: одна возвращает количество строк в таблице, вторая создавала ячейки и описывала их содержимое.
Обновим эти функции, в соответствии с новым функционалом.
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int { return tableData.count } func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! { let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "MyTestCell") var rowData: NSDictionary = self.tableData[indexPath.row] as NSDictionary cell.text = rowData["trackName"] as String // Обращаемся к ключу artworkUrl60 для получения ссылки на обложку объекта var urlString: NSString = rowData["artworkUrl60"] as NSString var imgURL: NSURL = NSURL(string: urlString) // Скачиваем файл обложки в объект NSData для последующего отображения в ячейке var imgData: NSData = NSData(contentsOfURL: imgURL) cell.image = UIImage(data: imgData) // Получаем цену объекта по ключу formattedPrice и отображаем ее в качестве subtitle var formattedPrice: NSString = rowData["formattedPrice"] as NSString cell.detailTextLabel.text = formattedPrice return cell }
Функция numberOfRowsInSection теперь просто возвращает количество полученных в ответе объектов.
Функция cellForRowAtIndexPath вместо отображения номера строки теперь отображает название объекта, его обложку и стоимость.

Если все хорошо и у вас получилось запустить приложение, возможно вы заметите некоторые «лаги» во время его работы. Это связано с тем, что мы не предусмотрели несколько вещей, которым уделим внимание в следующей статье.
P.S.: После выхода моего перевода, автор несколько обновил урок, немного оптимизировав код. Перевод я постарался привести в соответствие.

