Загрузка данных — NSURLSession

До выхода iOS 7.0 единственным интерфейсом для загрузки данных по сети служил класс NSURLConnection, затем был добавлен NSURLSession, который со временем стал полноценным преемником, обладающим расширенными и более гибкими возможностями. Начиная с iOS 9.0 NSURLConnection более не рекомендуется использовать.

NSURLSession поддерживает основные протоколы передачи информации, в том числе защищенные. Работа осуществляется как с обычными файлами, так и объектами NSData.

Для простых задач у класса есть метод NSURLSession.sharedSession(), возвращающий преднастроенный синглтон-экземпляр. Настройки по умолчанию достаточно оптимальны, единственным серьезным минусом является невозможность задать делегата, отслеживающего все события процесса, а не только завершение загрузки посредством функции-обработчика.

import Foundation
import XCPlayground

// Playground configuration
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

let url = NSURL(string: "http://valery.bashkatov.org/favicon.png")!
let session = NSURLSession.sharedSession()

let task = session.dataTaskWithURL(url,
                                   completionHandler: 
                                   // This function is called when the task is completed
                                   {
                                       (data: NSData?,
                                        response: NSURLResponse?,
                                        error: NSError?) -> Void in
                                       if error == nil {
                                           print("Downloading completed")
                                       } else {
                                           print(error)
                                       }
                                   })

// Run task
task.resume()

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

Затем возможно изменить неустраивающие значения свойств.

Во время инициализации NSURLSession разрешается также указать делегата, получающего контроль над процессом загрузки через реализацию протоколов:

import UIKit

class ViewController: UIViewController, NSURLSessionDataDelegate {
    var data: NSMutableData!
    var dataURL: NSURL!
    var session: NSURLSession!
    var sessionConfiguration: NSURLSessionConfiguration!
    var sessionTask: NSURLSessionDataTask!

    override func viewDidLoad() {
        super.viewDidLoad()

        data = NSMutableData()
        dataURL = NSURL(string: "http://valery.bashkatov.org/favicon.png")!
        
        // Used ephemeral session for disable disk-based cache
        sessionConfiguration = NSURLSessionConfiguration.ephemeralSessionConfiguration()
        sessionConfiguration.timeoutIntervalForRequest = 10
        session = NSURLSession(configuration: sessionConfiguration,
                               delegate: self,
                               delegateQueue: nil)

        sessionTask = session.dataTaskWithURL(dataURL)
        sessionTask.resume()
    }

    // Method defined in NSURLSessionDataDelegate protocol
    func URLSession(session: NSURLSession,
                    dataTask: NSURLSessionDataTask,
                    didReceiveResponse response: NSURLResponse,
                    completionHandler: (NSURLSessionResponseDisposition) -> Void) {
        print("Received server response")
        print("Expected file size: \(response.expectedContentLength) bytes")

        completionHandler(NSURLSessionResponseDisposition.Allow)
        print("Downloading started")
    }

    // Method defined in NSURLSessionDataDelegate protocol
    func URLSession(session: NSURLSession,
                    dataTask: NSURLSessionDataTask,
                    didReceiveData data: NSData) {
        print("  Received: \(data.length) bytes")

        // Collect full data, chunk by chunk
        data.enumerateByteRangesUsingBlock
        {
            [unowned self] (bytes: UnsafePointer<Void>,
                            byteRange: NSRange,
                            stop: UnsafeMutablePointer<ObjCBool>) in
            self.data.appendBytes(bytes, length: byteRange.length)
        }
    }

    // Method defined in NSURLSessionTaskDelegate protocol
    func URLSession(session: NSURLSession,
                    task: NSURLSessionTask,
                    didCompleteWithError error: NSError?) {
        guard error == nil else {
            print(error)
            return
        }
        print("Downloading completed")
    }
}