Alamofire: Часть 1 (Xcode 6.3)

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

AFNetworking одна из самых известных сторонних библиотек для iOS и OS X. Она получила награду, как лучшая библиотека 2012 года, по версии Reader's Choice Awards. А так же она является одним из самых распространенных open-source проектов на Github, где имеет более 14 тысяч звезд, и более 4 тысяч вилок.

Недавно, основатель AFNetworking Mattt Thompson выпустил новую библиотеку для работы в сети, предназначенную специально для современных конвенций Swift и его особенностей, которая называется Alamofire.

Префикс AF в AFNteworking является сокращением от Alamofire, так что даже имя новой библиотеки основывается на конвециях Swift! :]

Первая часть этого двухсерийного туториала покажет вам, как вы можете использовать Alamofire для создания приложения фотогалереи, которое использует ресурс 500px.com в качестве источника фотографий. Ну, и по мере чтения, вы научитесь использовать самые важные компоненты Alamofire и другие очень важные аспекты обработки сетевых запросов в ваших приложениях.

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

Этот туториал подразумевает, что вы знакомы с разработкой под iOS. Если вы все таки не знакомы, то пройдите первые туторалы для ознакомления.

Заметка

Если вы знакомы с основами использования Alamofire, то вы можете сразу приступить ко второй части (если она уже опубликована). Но убедитесь в том, что у вас есть пользовательский ключ, который мы должны будем использовать ниже.

Поехали!

Скачайте стартовый проект для этого туториала. В нем находится весь необходимый код для UI, чтобы можно было закончить этот туториал. Давайте сосредоточимся на изучении Alamofire, вместо того, чтобы изучать дизайн UI.

Откройте проект Xcode и взгляните на Main.storyboard:

Приложение использует общий UI шаблон вместе с UITabBarController в качестве стартового экрана. Сам tab controller содержит две вкладки, каждая из которых является UINavigationController. Первая вкладка позволяет пользователям просматривать популярные фотографии, а вторая позволяет просматривать фотографии, которые они сами сохранили. Обе вкладки отображают фотографии в UICollectionVewController. Так же, сториборд содержит два отдельных view controllers, которые будут использованы позднее в этом туториале.

Пока что наше приложение выглядит плохо, но вскоре мы изменим это, при помощи кода Alamofire.

Заметка

Если вы знакомы с AFNetworking, то скорее всего ожидаете, что следующая секция расскажет вам о CocoaPods. Да, с CocoaPods 0.36 и выше, вы можете использовать встроенные библиотеки, написанные на Swift, но есть ограничение iOS 8.0+.

Для этого туториала мы будем использовать альтерантивный способ, который не требует CocoaPods, но вы можете спокойно использовать CocoaPods, если вы с ним уже знакомы.

Для того, чтобы получить последнюю версию Alamofire, обратитесь в соответствующую ветку https://github.com/Alamofire/Alamofire и нажмите кнопку Download ZIP, которую вы найдете на правой стороне страницы. Откройте папку стартового проекта в Finder и перетащите туда папку Alamofire-master.

Откройте папку Alamofire-master (теперь которая у вас в поддиректории вашего проекта) и перетащите Alamofire.xcodeproj в ваш проект Photomania:

Теперь кликните на Photomania и убедитесь, что вы выбрали вкладку General. Прокрутите вниз до пункта Embedded Binaries, нажмите +, а затем выберите Alamofire.framework и нажмите Add:

Запустите проект и убедитесь, что он работает без ошибок.

Получаем данные с помощью Alamofire

Скорее всего у вас возник вопрос касательно  Alamofire. И правда, зачем нужен Alamofire? Ведь Apple предоставляет нам класс NSURLSession и соответствующие классы, при помощи которых мы можем скачивать контент через HTTP, так зачем же усложнять вещи через сторонние библиотеки?

У нас есть такой ответ: "Alamofire основан на NSURLSession, но так же она освобождает нас от написания шаблонного кода, что делает написание сетевого кода сравнительно простой задачей." Вы можете получить доступ к данным из интернета, приложив минимум усилий, что значит, что ваш код будет более чистым и более удобным для чтения.

Для использования Alamofire сначала нужно сделать импорт. Для того, чтобы это сделать, вам нужно открыть PhotoBrowserCollectionViewController.swift и добавить в него, на самый верх, одну строку:

import Alamofire

Вы это делаете для того, чтобы импортировать выражение в каждый файл, который получает доступ к классам и функциям Alamofire.

Далее, добавим следующий код внутрь viewDidLoad(), сразу после setupView():

Alamofire.request(.GET, "https://api.500px.com/v1/photos").responseJSON() {
  (_, _, data, _) in
  println(data)
}

Мгновение позже мы разберем этот код, но сначала запустите свое приложение и посмотрите на сообщение, которое появится в консоли:

Optional({
    error = "Consumer key missing.";
    status = 401;
})

Если вы не поняли, вы только что сделали ваш первый сетевой запрос при помощи Alamofire. Вы запросили источник в интернете и получили в ответ JSON.

Вот что происходит на самом деле:

  • Alamofire.request(_:_) принимает два обязательных параметра: первый - метод, который обычно .GET или .POST, и URLString, обозначающий URL адрес контента, к которому вы хотите получить доступ. Затем вы получаете объект Alamofire.Request в качестве ответа.
  • Обычно вы объединяете объект запроса с методом запроса. Например, в коде выше, объект запроса просто вызывает responseJSON().responseJSON, которое вызовет замыкание (closure), когда наш запрос завершится. В этом коде вы просто выводите отпарсенный JSON в консоль.
  • Вызывая responseJSON, вы показываете, что вы ожидаете ответ в виде JSON. В этом случае Alamofire предпринимает попытку отпарсить ответ и вернуть вам объект JSON. Альтернативно, вы можете запросить список свойств, используя responsePropertyList или даже сырые строки, при использовании responseString. Но об это мы поговорим чуточку позже.

Как вы можете видеть из этого ответа, отпечатанного в консоли, сервер говорит, что ему нужно что-то вроде conusmers key. Перед тем как мы пойдем дальше, вам понадобится ключ для 500px API.

Получение вашего Consumer Key

Сначала пройдите по ссылке и зарегистрируйтесь: 500px.com/signup.

После того, как вы закончили с регистрацией, пройдите по: https://500px.com/settings/applications. Нажмите "Register your application".

Вы увидите следующую форму:

Эти поля, с большими красным стрелочками, обязательны. Добавьте Application Name как Almofire Tutorial, а Desctription, как iOS App. Ваше приложение не имеет Application URL, но введите любой валидный URL, для того, чтобы удовлетворить требования регистрации приложения. Можете даже указать iphonecoder.ru и никто на вас не обидится! ;]

Введите свой e-mail адрес в форме Developer's Email и нажмите на чекбокс, для принятия правил использования сервиса.

А теперь, нажмите на кнопку Register и вы увидите примерно вот что:

Нажмите на кнопку See application details и это окно растянется. Далее, мы покажем вам consumer key:

Скопируйте consumer key с этого экрана, затем вернитесь в Xcode, и замените первый кусок кода, который вы уже добавили, на следующий:

Alamofire.request(.GET, "https://api.500px.com/v1/photos", parameters: ["consumer_key": "PASTE_YOUR_CONSUMER_KEY_HERE"]).responseJSON() {
  (_, _, JSON, _) in
  println(JSON)
}

Убедитесь, что вы заменили надпись "PASTE_YOUR_CONSUMER_KEY_HERE" вашим ключом, который вы скопировали чуть выше.

Запустите свое приложение, теперь вы видите намного больше выходных данных в консоли:

Все выходные данные сверху означают, что вы успешно скачали JSON, который содержит информацию о некоторых фотографиях.

Ответ JSON содержит несколько свойств картинок, некоторую информацию о страницах, и массив фотографий. Ниже представлен результат, который я получил (ваши реузультаты могут отличаться от моих):

{
  "feature": "popular",
  "filters": {
      "category": false,
      "exclude": false
  },
  "current_page": 1,
  "total_pages": 250,
  "total_items": 5000,
  "photos": [
    {
      "id": 4910421,
      "name": "Orange or lemon",
      "description": "",
	.
	.
	.
      }
    },
    {
      "id": 4905955,
      "name": "R E S I G N E D",
      "description": "From the past of Tagus River, we have History and memories, some of them abandoned and disclaimed in their margins ...",
	.
	.
	.
    }
  ]
}

Теперь, когда вы получили JSON, вы можете сделать что-нибудь полезное с ним.

Замените println(JSON) в viewDidLoad() следующим кодом:

let photoInfos = (JSON!.valueForKey("photos") as! [NSDictionary]).filter({
    ($0["nsfw"] as! Bool) == false
  }).map {
    PhotoInfo(id: $0["id"] as! Int, url: $0["image_url"] as! String)
  }
 
self.photos.addObjectsFromArray(photoInfos)
 
self.collectionView!.reloadData()

Код выше переводит JSON в формат массива более управляемых объектов PhotoInfo. Эти объекты являются пустыми "ведрами" для ID фотографий и URL свойств. Вы так же заметили, что в коде имеется некий фильтр, который отфильтровывает картинки.

Код выше так же перезагружает collectionView.

Запустите ваше приложение. На этот раз значок загрузки пропадает через какое-то время, и у нас появляется темно серые ячейки с черными разделителями:

Мы уже близко к нашей цели!

В PhotoBrowserCollectionViewController.swift добавьте дополнительный код в collectionView(_: cellForItemAtIndexPath:) прямо перед return cell:

let imageURL = (photos.objectAtIndex(indexPath.row) as! PhotoInfo).url
 
Alamofire.request(.GET, imageURL).response() {
  (_, _, data, _) in
 
  let image = UIImage(data: data!)
  cell.imageView.image = image
}

Этот код создает еще один Alamofire запрос картинки для массива photos. Так как этот запрос для картинки, то вы используете простой метод request, который возвращает вам ответ в виде NSData. Затем вы отдаете данные напрямую в экземпляр UIImage, что в свою очередь устанавливает картинку в image view, который уже существует в этом фрагменте проекта.

Запустите еще раз ваше приложение. Должна появиться целая галерея картинок, что-то типа изображения ниже:

У вас теперь есть свое вещественное доказательство того, что концепция Alamofire работает просто отлично! Но вы наверное не хотите чтобы каждый раз, как вы обращаетесь на сервер с каким-либо запросом, вам приходилось вставлять API адрес и ваш ключ. Да и кроме того, такой код будет более уродливым и громоздким, и просто представьте себе с какой переработкой кода вам придется столкнуться, когда вы обнаружите, что адрес API изменился, или вам придется использовать новый ключ!

К счастью, у Alamofire есть решение именно этой проблемы.

Создаем роутер для запросов

Откройте Five100px.swift и найдите в нем struct Five100px, который определяет enum ImageSize. Это очень простая структура данных, основанная на документации 500px.com API.

Так как вы добавляете определенный код из Alamofire, то вы должны импортировать его в файл:

import Alamofire

А теперь добавьте код внутрь struct Five100px, сразу перед ImageSize:

enum Router: URLRequestConvertible {
  static let baseURLString = "https://api.500px.com/v1"
  static let consumerKey = "PASTE_YOUR_CONSUMER_KEY_HERE"
 
    case PopularPhotos(Int)
    case PhotoInfo(Int, ImageSize)
    case Comments(Int, Int)
 
    var URLRequest: NSURLRequest {
      let (path: String, parameters: [String: AnyObject]) = {
        switch self {
        case .PopularPhotos (let page):
          let params = ["consumer_key": Router.consumerKey, "page": "\(page)", "feature": "popular", "rpp": "50",  "include_store": "store_download", "include_states": "votes"]
          return ("/photos", params)
        case .PhotoInfo(let photoID, let imageSize):
          var params = ["consumer_key": Router.consumerKey, "image_size": "\(imageSize.rawValue)"]
          return ("/photos/\(photoID)", params)
        case .Comments(let photoID, let commentsPage):
          var params = ["consumer_key": Router.consumerKey, "comments": "1", "comments_page": "\(commentsPage)"]
          return ("/photos/\(photoID)/comments", params)
        }
        }()
 
        let URL = NSURL(string: Router.baseURLString)
        let URLRequest = NSURLRequest(URL: URL!.URLByAppendingPathComponent(path))
        let encoding = Alamofire.ParameterEncoding.URL
 
        return encoding.encode(URLRequest, parameters: parameters).0
  }
}

Это ваш роутер, который создает подходящие экземпляры URLString для ваших API вызовов. Это простой enum, который подписан на протокол URLRequestConvertable и который определен внутри Alamofire. Когда enum начинает соответствовать этому протоколу, вы должны иметь переменный тип NSURLRequest, который будет называться URLRequest.

Ваш роутер имеет две статические константы, baseURLString вашего API и ваш consumer key. В последний раз замените PASTE_YOUR_CONSUMER_KEY_HERE на ваш ключ. С этого момента роутер будет добавлять ваш ключ автоматически в URLString, когда это будет необходимо.

Ваше приложение имеет три конечные API: первая - получить список популярных фотографий, вторая - получить информацию о конкретной фотографии, третья - получить комментарии к фотографии. Ваш роутер обрабатывает три условия с тремя соответствующими case, которые имеют по одному или два параметра.

Вы определили var URLRequest: NSURLRequest как вычисляемое свойство, что означает, что вы используете перечисление (enum), которое собирает результирующий URL "на лету", основываясь на определенном case и его параметрах.

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

Five100px.Router.PhotoInfo(10000, Five100px.ImageSize.Large)
// URL: https://api.500px.com/v1/photos/10000?consumer_key=xxxxxx&image_size=4
// https://api.500px.com/v1  +  /photos/10000  +  ?consumer_key=xxxxxx&image_size=4
// = baseURLString  +  path  +  encoded parameters

В верхнем примере код идет по роутеру через инфо API фотографии, затем он ищет большую версию фотографии, где ID равно 10000. Закомментированные участки описывают конструкцию запроса. В нашем случае наш URL имеет три компонента: baseURLString, путь(который определяется между базовым URL и знаком вопроса "?"), и словарь типа [String: AnyObject] с параметрами, которые нужно передать вашему конечному API.

Для path первым элементом возвращаемого кортежа, вы используете простую интерполяцию строки:

"/photos/\(photoID)" // "/photos/10000"

Параметры запроса так же могут быть закодированы как JSON, список параметров или строк. Обычно вы используете простые строковые параметры, как в коде выше.

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

https://api.foursquare.com/v2/users/{USER_ID}/lists?v=20131016&group=created

Решение

static let baseURLString = "https://api.foursquare.com/v2"
 
case UserLists(Int)
 
var URLRequest: NSURLRequest {
  let (path: String, parameters: [String: AnyObject]) = {
    switch self {
    case . UserLists (let userID):
      let params = ["v": "20131016", "group": "created"]
      return ("/users/\(userID)/lists", params)
    }
  }()
.
.
.

Тут вы добавляете другой case для enum, для списка пользователей, и другой case внутрь конструкции switch, которая сама находится внутри URLRequest для того, чтобы установить правильно параметры и путь.

Ну как, справились? Если вы не были уверены на 100% в ответе, то потратьте еще немного времени и проанализируйте маршрут до тех пор, пока не будете чувствовать себя как рыба в воде!

Загружаем больше фотографий

На данный момент ваше приложение отображает только одну страницу с фотографиями. 

Откройте PhotoBrowserCollectionViewController.swift и добавьте следующий код сразу после let refreshControl = UIRefreshControl():

var populatingPhotos = false
var currentPage = 1

Здесь находятся две переменные, которые отслеживают, распространяете ли вы какие-либо фотографии и какая текущая страница фотографий.

Следующим шагом замените текущую реализацию viewDidLoad() следующим кодом:

override func viewDidLoad() {
  super.viewDidLoad()
 
  setupView()
 
  populatePhotos()
}

Здесь вы меняете запрос Alamofire, который вы имели, на вызов populatePhotos(), который вы напишите через пару минут.

Мы все еще работаем в том же файле, добавьте следующие две функции над handleRefresh():

// 1
override func scrollViewDidScroll(scrollView: UIScrollView) {
  if scrollView.contentOffset.y + view.frame.size.height > scrollView.contentSize.height * 0.8 {
    populatePhotos()
  }
}
 
func populatePhotos() {
  // 2
  if populatingPhotos {
    return
  }
 
  populatingPhotos = true
 
  // 3
  Alamofire.request(Five100px.Router.PopularPhotos(self.currentPage)).responseJSON() {
    (_, _, JSON, error) in
 
    if error == nil {
      // 4
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
        // 5, 6, 7
        let photoInfos = ((JSON as! NSDictionary).valueForKey("photos") as! [NSDictionary]).filter({ ($0["nsfw"] as! Bool) == false }).map { PhotoInfo(id: $0["id"] as! Int, url: $0["image_url"] as! String) }
 
        // 8
        let lastItem = self.photos.count
        // 9
        self.photos.addObjectsFromArray(photoInfos)
 
        // 10
        let indexPaths = (lastItem..<self.photos.count).map { NSIndexPath(forItem: $0, inSection: 0) }
 
        // 11
        dispatch_async(dispatch_get_main_queue()) {
          self.collectionView!.insertItemsAtIndexPaths(indexPaths)
        }
 
        self.currentPage++
      }
    }
    self.populatingPhotos = false
  }
}

В этом коде много чего происходит, так что давайте разберем секцию за секцией:

  • scrollViewDidScroll() подгружает больше фотографий, когда вы прокручиваете более 80% от окна.
  • populatePhotos() загружает фотографии на текущей currentPage и использует populatingPhotos как флажок, обозначающий, что она не будет грузить следующую страницу, пока грузит эту.
  • Вы используете ваш вымышленный ротур первый раз. Вы передаете ему номер страницы и конструктор URL строки для нее. 500px.com возвращает максимум по 50 фотографий на каждый вызов, так что вам нужно сделать еще один вызов, чтобы началась загрузка следующих 50 фотографий.
  • Сделайте себе аккуратную заметку, что завершающий обработчик - или последующее замыкание (trailing closure) .responseJSON() - всегда должно идти в основном потоке. Если вы выполняете длительные операции, например, создание API вызова, то вы должны использовать GCD для отправки вашего кода в другую очередь. В нашем случае вы используете DISPATCH_QUEUE_PRIORITY_HIGH для запуска этого процесса.
  • Вам интересен ключ photos в ответе JSON, который включает в себя массив словарей. Каждый словарь содержит информацию только об одном фото.
  • Здесь мы используем фильтр языка Swift filter для отфильтровки NSFW(not safe for work) картинок.
  • Функция map принимает замыкание и возвращает массив объектов PhotoInfo. Этот класс определен в Five100px.swift. Если вы посмотрите на код этого класса, то вы увидите, что он переписывает методы isEqual и hash. Оба этих метода используют целочисленные значения для id свойства, так что упорядочивание и присваивание уникального значения для PhotoInfo будут относительно быстрыми операциями.
  • Следующим шагом вы должны сохранить текущее значение количества фотографий до того, как вам предоставится еще одна пачка. Вы будете использовать это для обновления collectionView.
  • Если кто-либо загружает новую фотографию на 500px.com до того, как вы пролистали страничку вниз, то следующая порция подгружаемых фотографий может содержать и ту, которую только что залили. Именно поэтому вы определяете var photos = NSMutableOrderedSet() в качестве сета. Так как все элементы одного сета должны быть уникальными, то это гарантирует, что вы не получите дважды одно и то же фото.
  • Здесь вы создаете массив из объектов NSIndexPath, для внесения в них collectionView.
  • Вкладывает элементы в collection view по общей очереди, так как все операции UIKit должны быть выполнены по общей очередности.

Запустите ваше приложение, медленно прокрутите страницу вниз и вы увидите, как фотографии постепенно загружаются через ваше приложение:

А теперь прокрутите побыстрее, заметили некоторые изъяны? Да, прокрутка несколько прерывистая. Это не тот опыт, который бы вы хотели подарить вашим пользователям. Но мы разберемся с этим недостатком в следующей секции.

Создайние пользовательского сериализатора ответа

Вы уже видели, как просто можно использовать предоставленные сериализаторы JSON, string из списка параметров в Alamofire. Но иногда вы хотите создать ваш собственный, или индивидуальную сериалиазацию ответа. Например, вместо получения NSData, которую вы потом должны конвертировать в UIImage, вы можете написать пользовательский сериализатор ответа, чтобы напрямую конвертировать UIImage для вас.

В этой секции вы узнаете как это можно сделать.

Откройте Five100px.swift и добавьте следующий код, прямо под import Alamofire:

extension Alamofire.Request {
  public static func imageResponseSerializer() -> GenericResponseSerializer<UIImage> {
    return GenericResponseSerializer { request, response, data in
      if data == nil {
        return (nil, nil)
      }
 
      let image = UIImage(data: data!, scale: UIScreen.mainScreen().scale)
 
      return (image, nil)
    }
  }
 
  public func responseImage(completionHandler: (NSURLRequest, NSHTTPURLResponse?, UIImage?, NSError?) -> Void) -> Self {
    return response(responseSerializer: Request.imageResponseSerializer(), completionHandler: completionHandler)
  }
}

Для того, чтобы создать новый сериализатор ответа, сначала вам понадобится классовая функция, которая возвращает замыкание Serializer(в примере наверху imageResponseSerializer()). Это замыкание является псевдонимом или алиасом типа, который принимает три параметра и возвращает два, как показано ниже:

struct GenericResponseSerializer<T> : ResponseSerializer {
  typealias SerializedObject = T
  var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?) -> (T?, NSError?)
  init(serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?) -> (T?, NSError?))
}

Ваша классовая функция (imageResponseSerializer()) принимает в качестве аргумента запрос NSURLSession и объекты ответа вместе с сырым представлением NSData данных полученных от сервера. Затем функция использует эти объекты для сериализации входных значений в осмысленную структуру данных и возвращает ее вместе с любой ошибкой, которая может произойти в течение этого процесса. В вашем случае вы используете UIImage, для конвертирования данных в объект изображения.

Обычно, когда вы создаете сериализатор, вы так же хотите создать новый обработчик ответа, который будет вместе с сериализатором. Вы создаете его при помощи .responseImage(), которая имеет довольно простую работу. Этот метод берет completionHandler - блок кода, в виде замыкания, которое выполняется каждый раз, как вы сериализуете данные, полученные от сервера. Все что вам нужно в ваших обработчиках ответа - это вызвать .response(), который является главной идеей Alamofire.

Откройте PhotoBrowserCollectionViewController.swift и добавьте следующее свойство в PhotoBrowserCollectionViewCell под свойство imageView:

var request: Alamofire.Request?

Это сохранит запрос Alamofire для загрузки изображения для этой ячейки.

Теперь замените содержимое collectionView(_: cellForItemAtIndexPath:) следующим кодом:

let cell = collectionView.dequeueReusableCellWithReuseIdentifier(PhotoBrowserCellIdentifier, forIndexPath: indexPath) as! PhotoBrowserCollectionViewCell
 
let imageURL = (photos.objectAtIndex(indexPath.row) as! PhotoInfo).url
 
cell.imageView.image = nil
cell.request?.cancel()
 
cell.request = Alamofire.request(.GET, imageURL).responseImage() {
  (request, _, image, error) in
  if error == nil && image != nil {
    cell.imageView.image = image
  }
}
 
return cell

Запустите ваше приложение, прокрутите вниз. Теперь вы видите насколько плавнее у вас все стало двигаться:

Почему так работает быстрее?

Но, что именно вы поменяли, что приложение стало работать значительно быстрее? Переломный код находится в collectionView(_: cellForItemAtIndexPath:). Но перед тем, как вы сможете понять, как работает наше усовершенствование, вам нужно разобраться с природой асинхронности сетевых вызовов.

Сетевые запросы и соответственно, запросы Alamofire - являются асинхронными запросами. Это значит, что вы делаете так, что ваши сетевые запросы не останавливают выполнения оставшегося кода. Сетевые запросы потенциально могут занимать много времени для возврата ответа, так что вы не должны позволять вашему UI застывать в ожидании загрузки картинки!

Как говорится, что асинхронные запросы - это вызов. Что произойдет, если ваш UI изменился в промежутке между отправкой запроса и получением ответа от сервера?

Например, UICollectionView имеет внутренний механизм, который нарушает очередность ячеек. Создание новой ячейки обходится дорого и collectionView использует старые ячейки, которые уже вышли за границы экрана.

Это означает, что одни и те же объекты ячейки, с одним и тем же адресом в памяти, будут использованы снова и снова. Теперь, во временном промежутке, между отправкой запроса Alamofire и получением ответа от сервера, пользователь может прокрутить скрол, так что когда придет ответ, то может так оказаться, что изображение нам больше не нужно. Так что ячейка может быть вызвана из очереди, чтобы отобразить совершенно другую картинку!

Вы сделали две вещи для обработки ситуации. Первая, когда вы вызываете из очереди ячейку, вы присваиваете значение изображения nil, это гарантирует, что вы случайно не покажете предыдущее изображение. Второе, ваш обработчик завершения запроса проверяет соответствие URL запроса и URL ячейки. Если они не совпадают, то ячейка движется к другому изображению и ваш обработчик не будет терять свои циклы, устанавливая неверные изображения ячейке.

Что делаем дальше?

Вы можете скачать готовый проект первой части туториала Alamofire.

Вы многое изучили в этом туториале, теперь самое время сделать перерыв! На этом этапе ваше приложение имеет базовую функцию просмотра фотографий, благодаря Alamofire.

Тут вы научились делать запросы GET через Alamofire, отправлять параметры, создавать роутеры и даже создавать ваш собственный сериализатор ответа.

В следующей части мы добавим к нашей базовой функциональности следующее:

  • Просмотр фотографий
  • Возможность просматривать комментарии
  • Реализуем опцию скачивания фотографий с прогресс баром
  • И возможность обновления

Надеюсь, что этот туториал пришелся вам по вкусу и вы с нетерпением ждете второй части!

Что дальше?

Дальше, вы можете продолжить изучать наши туториалы по мере их появления, а также, параллельно читать перевод официальной книги по языку программирования Swift. И, для более подробного изучения языка, вы можете пройти наши курсы!

Урок подготовил: Иван Акулов

Источник урока: http://www.raywenderlich.com/85080/beginning-alamofire-tutorial