-1

Hi I am failry new to iOS development and wanted to know if there is a equivalent of asynctask in iOS? I want my web service to finish and then I want to pass the contents of my web service to nextview controller

@IBAction func searchAction(_ sender: Any) {

    let airportName: String = airportCode.text!
    let minutesBefore: String = minutesBehind.text!
    let minutesAfter: String = minutesAhead.text!

    //web service request

    self.data = FlightWebService().getFlightData(airportCode: airportName, minutesBehind: minutesBefore, minutesAhead: minutesAfter)
    print(self.data)


    //how to pass data to the next view controller
    performSegue(withIdentifier: "goToSearch", sender: self)
}
Ackman
  • 1,562
  • 6
  • 31
  • 54
  • Your flight service class should be doing the async part, you shouldn't be using self.data. the getFlightData function should be async and use a completion handler to pass the data back once received/parsed – Scriptable Mar 21 '18 at 08:25
  • If you are looking for AsyncTask in iOS, [here](https://stackoverflow.com/a/19606761/2835520) is how to implement it. – IgniteCoders Mar 21 '18 at 13:04

3 Answers3

2

You Can use URLSession available from ios 7.0 and later.

In your method you can run async task

func searchAction() {
    let defaultSessionConfiguration = URLSessionConfiguration.default
    let defaultSession = URLSession(configuration: defaultSessionConfiguration)

    let url = URL(string: "typeHereYourURL")
    var urlRequest = URLRequest(url: url!)

    let params = ""// json or something else
    let data = params.data(using: .utf8)

    urlRequest.httpMethod = "POST"
    urlRequest.httpBody = data

    let dataTask = defaultSession.dataTask(with: urlRequest) { (data, response, error) in
        performSegue(withIdentifier: "YourVCIdentifier", sender: self)
    }

    dataTask.resume()
}

or you can create serial queue and run FlightWebService request in another thread

func searchAction() {
    let newQueue = DispatchQueue(label: "queue_label")
    newQueue.async {
        self.data = FlightWebService().getFlightData(airportCode: airportName, minutesBehind: minutesBefore, minutesAhead: minutesAfter)
        print(self.data)

        DispatchQueue.main.async {
            performSegue(withIdentifier: "YourVCIdentifier", sender: self)
        }
    }
}

and override this to send parameters to the next ViewController

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "YourVCIdentifier" {
        if let destinationVC = segue.destination as? YourVC {
            destinationVC.exampleStringProperty = //send data here recived earlier
        }
    }
}
Jabson
  • 1,585
  • 1
  • 12
  • 16
0

Based upon @SH_Khan's request I've placed your URLRequest call inside the FlightWebService class. That should do the trick.

class FlightWebService {

    // Change the method's signature to accept a completion block
    static func getFlightData(airportCode: String, minutesBehind: String, minutesAhead: String, completion: (Data?, Response?, Error?) -> Void) {
        let defaultSessionConfiguration = URLSessionConfiguration.default
        let defaultSession = URLSession(configuration: defaultSessionConfiguration)

        let url = URL(string: "typeHereYourURL")
        var urlRequest = URLRequest(url: url!)

        let params = ""// json or something else
        let data = params.data(using: .utf8)

        urlRequest.httpMethod = "POST"
        urlRequest.httpBody = data

        // Launch the task **IN BACKGROUND** with the provided completion handler block
        DispatchQueue.global(qos: .background).async {
            let dataTask = defaultSession.dataTask(with: urlRequest, completionHandler: completion) 
            dataTask.resume()
        }
    }
}

@IBAction func searchAction(_ sender: Any) {

    let airportName: String = airportCode.text!
    let minutesBefore: String = minutesBehind.text!
    let minutesAfter: String = minutesAhead.text!

    //web service request
    FlightWebService().getFlightData(airportCode: airportName, minutesBehind: minutesBefore, minutesAhead: minutesAfter) { (data, response, error) in
        self.data = data
        print(self.data)
        // Always perform view-related stuff on the main thread
        DispatchQueue.main.async {
            performSegue(withIdentifier: "goToSearch", sender: self)
        }
    }
}
Jake
  • 18
  • 2
0

Your FlightService functions need to be doing the async tasks and using completionHandlers to pass the response back to the calling code.

See the below code

// model
class Flight {}

// flight service
class FlightService: NSObject
{
    func getFlightData(airportCode: String, minutesBehind: Int, minutesAhead: Int, completion: @escaping (Bool, [Flight]?) -> Void) {
        // main thread still
        let url = NSURL(string: "http://..........")!
        let request = NSMutableURLRequest(url: url as URL)
        request.httpMethod = "Get"

        // This always runs in the background, so no need to specifically put it there
        let task = URLSession.shared.dataTask(with: request as URLRequest){ data,response,error in
            guard error == nil else {
                completion(false, nil)
                return
            }

            // parse response data instead I'll create blank array
            var parsedFlights = [Flight]()
            completion(true, parsedFlights)
        }

        task.resume()
    }
}

// USAGE

@IBAction func searchAction(_ sender: Any) {

    let airportName: String = "airportCode.text!"
    let minutesBefore: String = "minutesBehind.text!"
    let minutesAfter: String = "minutesAhead.text!"

    FlightWebService().getFlightData(airportCode: airportName, minutesBehind: minutesBefore, minutesAhead: minutesAfter) { [weak self] success, flights in
        // we are still in a background thread here and would need to Dispatch to the main thread to update any UI
        guard let strongSelf = self, success else { return }

        print(flights.count)
        strongSelf.flightSearchResults = flights
        strongSelf.performSegue(withIdentifier: "goToSearch", sender: self)
    }
}

NSURLSession always runs in the background thread so there is no need to manually dispatch into it.

In the searchFunction once the completionHandler returns we are still running in the background thread at this point but we have parsed our response data and passed back the flight data, if we need to update any UI from here we would need to dispatch into the main thread.

NOTE: you should generally also use a weak self in a closure because during the async task, we may have lost the reference to the calling class (self).

Scriptable
  • 19,402
  • 5
  • 56
  • 72