Получение данных из завершения Handler в Swift в NSURLConnection

Я пытаюсь написать функцию, которая будет выполнять асинхронный запрос GET и возвращать ответ (как любой тип данных, но здесь он как NSData).

Этот вопрос основан на: Как использовать завершение NSURLConnectionHandler с Swift

func getAsynchData() -> NSData {
    var dataOutput : NSData
    let url:NSURL = NSURL(string:"some url")
    let request:NSURLRequest = NSURLRequest(URL:url)
    let queue:NSOperationQueue = NSOperationQueue()

    NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
            /* this next line gives the below error */
            dataOutput = data
    })
    return dataOutput
}

но я получаю ошибку:

error: variable 'dataOutput' captured by a closure before being initialized

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

Я посмотрел на: Как использовать завершение Handler Закрытие с возвратом в Swift? но это не совсем отвечает на мой вопрос. Моя цель здесь - получить данные из моего асинхронного запроса из блока для использования в другом месте моего кода. Должен ли я выполнять всю работу с этим запросом в этом блоке, а не выводить данные?

Спасибо!

РЕДАКТИРОВАТЬ

Итак, у меня есть вариант, который, я думаю, может сработать, но мне он не кажется правильным. Может кто-нибудь сказать мне, если это лучший способ достижения моей цели?

func doThingsWithData( data: NSData ) -> String {
    /* code to extract string from NSData */
    return somestring
}
func getAsynchData() {
    let url:NSURL = NSURL(string:"some url")
    let request:NSURLRequest = NSURLRequest(URL:url)
    let queue:NSOperationQueue = NSOperationQueue()

    NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
            /* this next line gives the below error */
            doThingsWithData(data)
    })
}

РЕДАКТИРОВАТЬ 2 -> в ответ на отмену

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

class GetInfoFromAPI {

    func getSpecificValue(index : String) -> String {
        /* I assume I need to send the values from this function, yea? but how do I get them here? */
    }

    func doThingsWithData( data: NSData ) -> String {
        /* code to extract string from NSData */
        var error: NSError?
        let jsonDict = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) as NSDictionary

        specificValue1 : String = jsonDict.valueForKey("value1") as String
        specificValue2 : String = jsonDict.valueForKey("value2") as String
        specificValue3 : String = jsonDict.valueForKey("value3") as String

        /* I want to get these ^^^ values into the ViewController below */
    }

    func getAsynchData() {
        let url:NSURL = NSURL(string:"some url")
        let request:NSURLRequest = NSURLRequest(URL:url)
        let queue:NSOperationQueue = NSOperationQueue()

        NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
            /* this next line gives the below error */
            doThingsWithData(data)
        })
    }
}


class ViewController: UIViewController {

    @IBOutlet var labelVariable1: UILabel
    @IBOutlet var labelVariable2: UILabel
    @IBOutlet var labelVariable3: UILabel

    let apiInstance = GetInfoFromAPI()

    @IBAction func buttonTapped(sender : AnyObject) {
        labelVariable1 = apiInstance.getSpecificValue(1)
        labelVariable2 = apiInstance.getSpecificValue(2)
        labelVariable3 = apiInstance.getSpecificValue(3)
    }

}

Спасибо, что нашли время ответить на мои вопросы. Я новичок в Swift, и переполнение стека очень полезно!

1 ответ

Позвольте мне попытаться объяснить это - это неправильное понимание потоков, с которым я сам сначала боролся. Я попытаюсь немного упростить вещи. Когда вы запускаете этот код:

NSLog("Log 1")

NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
        NSLog("Log in Completion Block")
})

NSLog("Log after request")

Вы получите вывод, который выглядит следующим образом:

Log 1
Log after request
Log in completion block

Позвольте мне сделать диаграмму, вроде графика времени:

                    "Log 1" (before request is sent)
                                       |
                                       |
                           Request sent over Internet
                                       |
                                     /   \  
                   "Log after request"   |
               This is logged *before*   | 
           the other one, because this   |
          one doesn't have to wait for   |
               the network to respond.   |
                                         |
The method finishes and returns a value. |
------------------------------------------------------------------------------
                                         | The network finally responds,
                                         | and the completion block is run.
                                         |

                                         "Log in completion block"

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

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

Да по сути. Я могу помочь больше, если вы покажете мне код, который вызывает getAsynchData() на первом месте.

Другие вопросы по тегам