NSURLSession не возвращает данные при первом вызове

В общем, надо реализовать класс для сети. Это класс, который будет принимать URL и передавать данные. Все это сделано для того, чтобы не забивать лишнюю логику контроллеров. Столкнулся с такой проблемой, что при первом создании View данные не приходят. Это сетевой класс:

private static var dataTask: NSURLSessionDataTask?

private static var dataJSON: NSData?

private static var sessionConfig: NSURLSessionConfiguration = {
    var configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
    configuration.allowsCellularAccess = false
    configuration.HTTPMaximumConnectionsPerHost = 2
    configuration.HTTPAdditionalHeaders = ["Accept": "application/json"]
    configuration.timeoutIntervalForRequest = 30.0
    configuration.timeoutIntervalForResource = 60.0
    return configuration
}()


static func getListObjectsBy(url: String?) -> NSData? {
    let session = NSURLSession(configuration: sessionConfig)
    log.debug("DataTask start")
    dataTask = session.dataTaskWithURL(NSURL(string: url!)!) { (data, response, error) in
        log.debug("if error = error")
        if let error = error {
            print(error.localizedDescription)
        } else if let httpResponse = response as? NSHTTPURLResponse {
            log.debug("if httpResponse")
            if httpResponse.statusCode == 200 {
                dataJSON = data
            } else {
                print("Bad request")
            }
        }
    }
    dataTask?.resume()
    log.debug("DataTask Resume")
    return dataJSON
}

Метод viewDidLoad в моем основном контроллере:

let response = Network.getListObjectsBy("http://lb.rmc.su/api-dev/v2/wc/5")
print(String(response))

Мой журнал говорит мне, что данные возвращают ноль. Примечания, я переключаюсь между контроллерами с помощью SWRevealViewController. При перезагрузке контроллера основного представления данные возвращаются. Что мне делать?

введите здесь описание изображения


person Vladislav Prusakov    schedule 19.03.2016    source источник


Ответы (1)


Вы, кажется, неправильно понимаете, что это асинхронный вызов.

static func getListObjectsBy(url: String?) -> NSData? {
    let session = NSURLSession(configuration: sessionConfig)
    log.debug("DataTask start")
    dataTask = session.dataTaskWithURL(NSURL(string: url!)!) { (data, response, error) in
        // Everything in this block is happening on a separate thread.
        log.debug("if error = error")
        if let error = error {
            print(error.localizedDescription)
        } else if let httpResponse = response as? NSHTTPURLResponse {
            log.debug("if httpResponse")
            if httpResponse.statusCode == 200 {
                // this won't happen until the data comes back from the remote call.
                dataJSON = data
            } else {
                print("Bad request")
            }
        }
    }
    // This code here does not wait for the response from the remote.
    // The call to the remote is sent then this code 
    // is immediately executed WITHOUT WAITING
    dataTask?.resume()
    log.debug("DataTask Resume")
    // dataJSON will be nil until the remote answers.
    return dataJSON
}

Когда вы делаете это:

let response = Network.getListObjectsBy("http://lb.rmc.su/api-dev/v2/wc/5")
print(String(response))

Пульт еще не ответил, поэтому вы получите ноль.

Ваш следующий вопрос может быть «Что мне с этим делать?». Ответ не ясен, не зная всего остального, что вы делаете.

Потоки

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

Вот что здесь происходит.
Настраивает запрос для отправки в операционную систему.

dataTask = session.dataTaskWithURL(NSURL(string: url!)!)

Приказывает операционной системе начать работу.

dataTask?.resume()

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

dataTask = session.dataTaskWithURL(NSURL(string: url!)!) { 
    // Closure starts here
    // Gets called when the remote has sent a response.
    (data, response, error) in
    // Everything in this block is happening on a separate thread.
    log.debug("if error = error")
    etc
}

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

public typealias CompletionHandler = (data: NSData?, error: NSError?) -> Void

static func getListObjectsBy(url: String?, completion: CompletionHandler) {
    let session = NSURLSession(configuration: sessionConfig)
    log.debug("DataTask start")
    dataTask = session.dataTaskWithURL(NSURL(string: url!)!) { 
        (data, response, error) in
        // Everything in this block is happening on a separate thread.
        log.debug("if error = error")
        if let error = error {
            print(error.localizedDescription)
        } else if let httpResponse = response as? NSHTTPURLResponse {
            log.debug("if httpResponse")
            if httpResponse.statusCode == 200 {
                // this won't happen until the data comes back from the remote call.
            } else {
                print("Bad request")
            }
        }
        // Call your closure
        completion(data, error)
    }
    // This code here does not wait for the response from the remote.
    // The call to the remote is sent then this code 
    // is immediately executed WITHOUT WAITING
    dataTask?.resume()
    log.debug("DataTask Resume")
}

В вашем вызывающем коде вы должны сделать это:

Network.getListObjectsBy("http://lb.rmc.su/api-dev/v2/wc/5") {
    (data, error)  in
    if let data == data {
        print(data)
    }
}
person ryantxr    schedule 19.03.2016
comment
Я больше ничего не делаю :) Это весь код. К сожалению, я не очень понимаю, когда использовать асинхронную отправку. Что мне с этим делать? - person Vladislav Prusakov; 19.03.2016
comment
Я думаю, что ваш ответ правильный, тем не менее вы можете немного подробнее объяснить, как использовать замыкания для передачи данных в асинхронном вызове, чтобы больше помочь в вопросе. - person Victor Sigler; 19.03.2016
comment
Благодарю вас! Теперь я все понимаю! :) - person Vladislav Prusakov; 20.03.2016