Использование делегированных и протоколов для возврата данных из dataTaskWithRequest в iOS Swift

У меня есть фреймворк, который вызывает API, который возвращает мне JSON. Проблема в том, что у меня есть функция внутри моего SDK, которая использует NSURLSession и вызывает DataTaskWithRequest для получения данных. Проблема в том, что моя функция возвращает значение еще до того, как данные будут правильно выбраны. Я попытался использовать обработчик завершения с DataTaskWithRequest и в настоящее время использую цикл while для ожидания получения данных. Есть ли другой возможный способ сделать это

Код

public func getDetails() -> AnyObject {
    if Reachability.isConnectedToNetwork()
    {
        received = false
        let baseUrl = "API_URL  Here"
        let url = NSURL(string: baseUrl)
        let request = NSURLRequest(URL: url!)
        let session = NSURLSession.sharedSession()

        print("Starting the connection")
        let task = session.dataTaskWithRequest(request, completionHandler : {
            (data, response, error ) in

            //Proper JSON Form

            do
            {
                self.JSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
            }
            catch
            {
                print("Error")
                return
            }

            self.received = true

        })
        task.resume()

        print("Going through the loop")
        while(!received)
        {
            print("Searching")
        }
        print(JSON)
            return JSON
    }
    else
    {

        let alert = UIAlertView(title: "No Internet Connection", message: "Make sure your device is connected to the internet.", delegate: nil, cancelButtonTitle: "OK")
        alert.show()
        return 0

    }
}

person Sachindeep Singh    schedule 22.03.2016    source источник
comment
выложи свой код...   -  person EI Captain v2.0    schedule 22.03.2016


Ответы (2)


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

let myData = getDetails(blah)
doSomeAction(myData)

Способ приблизиться к этому — использовать блок завершения в качестве самого индикатора и сделать все асинхронным. Этот код не тестировался

public typealias CompletionHandler = (success: Bool, message: String, json: JSON?, error: NSError?) -> Void

func getDetailsAsync(completion: CompletionHandler) {
    if Reachability.isConnectedToNetwork()
    {
        received = false
        let baseUrl = "API_URL  Here"
        let url = NSURL(string: baseUrl)
        let request = NSURLRequest(URL: url!)
        let session = NSURLSession.sharedSession()

        print("Starting the connection")
        let task = session.dataTaskWithRequest(request, completionHandler : {
            (data, response, error ) in

            //Proper JSON Form
            if error != nil {
                completion(false, "error", nil, error)
            }
            else {
                do
                {
                    self.JSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
                    completion(true, "OK", self.JSON, nil)
                }
                catch
                {
                    print("Error")
                    completion(false, "JSON parsing failed", nil, nil)
                }
            }

        })
        task.resume()
    }
    else {
        completion(false, "No network", nil, nil)
    }
}

Назовите это так:

getDetailsAsync() {
    (success, message, json, error) in 
    if success == true {
        print(json)

    }
    else {
        // Do your alert here
        print(message)
        print(error)
    }
}

Этот ответ может быть полезен.

person ryantxr    schedule 22.03.2016
comment
Спасибо! Решил мою проблему - person Sachindeep Singh; 22.03.2016

Да, используйте другой поток для мониторинга процесса и заставьте его отправить сообщение через ваш протокол.

newQueue = NSOperationQueue()

    let operation2 = NSBlockOperation(block: {


        })

    operation2.completionBlock = {
        print("Operation 2 completed")
    }

    let operation1 = NSBlockOperation(block: {


        })

    operation1.completionBlock = {

        self.newQueue.addOperation(operation2)
    }

    operation1.qualityOfService  = .UserInitiated
    newQueue.addOperation(operation1)
}

Вот набросок кода. Короче говоря, вы запускаете сеанс с помощью блока 1, а по завершении запускаете проверку загрузки с помощью блока 2. блок 1 запускает блок 2, когда он завершается [почти сразу, как вы говорите], но блок 2 не срабатывает до тех пор, пока не будет загружена структура данных, завершающаяся в блоке 1.

person user3069232    schedule 22.03.2016
comment
Можете ли вы объяснить это немного подробнее, не знакомы с потоками в Swift - person Sachindeep Singh; 22.03.2016
comment
Это довольно прямолинейный Сингх. Все, что я здесь делаю, — это пишу код, который больше не течет сверху вниз, а идет от op1 к op2. Таким образом, addOperation запускает op1, когда он завершается, он запускает свой блок завершения, который затем добавляет операцию2, которая выполняется, а затем запускает свой блок завершения. Вы можете начать свой сеанс в операции 1 и отслеживать его завершение, когда вы знаете, что он выполняется в операции 2. - person user3069232; 22.03.2016
comment
Качество обслуживания здесь — это функция swift 2.0, сообщающая вашей IOS, каким должен быть приоритет указанного процесса. Поэтому я бы сделал операцию 2 здесь как можно ниже, если вы просто контролируете и выполняете операцию 1 на одну или две ступени выше, проверьте качество обслуживания NSOperations. - person user3069232; 22.03.2016
comment
Мне нравится подход, буду тестировать - person Sachindeep Singh; 22.03.2016