skip to Main Content

I’m trying to convert escaping closure code to async-await code in a certain file. More specifically, I’m trying to not use the completionHandler way of using the URLSession.shared.dataTask line of code in this file (which is specifically the line of code “URLSession.shared.dataTask(with: request) {” in the file that I’m working in).

In this file I’m making the change in, I’m getting the error: “Cannot find 'data' in scope” for line of code: “let json = try JSONSerialization.jsonObject(with: data!, options: [])”.

I think I’m getting this error because I never defined “data” further up in the file. I wasn’t getting this problem in the file that used an escaping closure for the overall code in the file’s function, and used a completionHandler for the “URLSession.shared.dataTask” line of code (which is specifically “URLSession.shared.dataTask(with: request) { (data, response, error) in”) in that file. Therefore, in the file that I’m working in, and that I have this error message in, I tried some solution attempts by creating the “data” variable right after the “URLSession.shared.dataTask(with: request) {” line of code, and tried two different variable assignments, however none of them worked. These solution attempts and the errors I got for them are in code snippets further below in this post. I made these solution attempts in the file that I’m working in, and that I have this error message in, which is File1-GetsDataNotUsingEscapingClosureAndNotUsingCompletionHandlerForURLSession.shared.dataTaskCode.swift, and they are included in that file further below, and are commented out.

I was expecting that since the data variable would have been created before the current line of code that has the error message (this line of code being “let json = try JSONSerialization.jsonObject(with: data!, options: [])” in File1-GetsDataNotUsingEscapingClosureAndNotUsingCompletionHandlerForURLSession.shared.dataTaskCode.swift) the code might be correct, and work, and the error message would go away.

How do I solve this error?

I’ve included the file that I’m currently working in that has the error mentioned above, and that doesn’t use an escaping closure way of doing things, and doesn’t use a completionHandler in the URLSession.shared.dataTask line of code (which is specifically “URLSession.shared.dataTask(with: request) {”), which is named File1-GetsDataNotUsingEscapingClosureAndNotUsingCompletionHandlerForURLSession.shared.dataTaskCode.swift. I’ve also included the solution attempts which are labeled “Solution Attempt Version 1” etc., and that include the errors that showed up after running the code with that solution attempt in this file.

I’ve also included the original file that uses the escaping closure way of doing things, of which uses a completionHandler in the URLSession.shared.dataTask line of code (which is specifically “URLSession.shared.dataTask(with: request) { (data, response, error) in”), which is named File2-GetsDataUsingEscapingClosureAndCompletionHandlerForURLSession.shared.dataTaskCode.swift.

Code:

File1-GetsDataNotUsingEscapingClosureAndNotUsingCompletionHandlerForURLSession.shared.dataTaskCode.swift:

import Foundation
import UIKit
import CoreLocation

extension UIViewController {

    func retrieveSelectedRestaurantDetailViewInfo(
        selected_restaurant_business_ID: String) async throws -> SelectedRestaurantDetailViewInfoNotUsingCodable? {

            // MARK: Make API Call
            let apiKey = "API key"

            /// Create URL
            let baseURL =
            "https://api.yelp.com/v3/businesses/(selected_restaurant_business_ID)"

            let url = URL(string: baseURL)

            /// Creating Request
            var request = URLRequest(url: url!)
            request.setValue("Bearer (apiKey)", forHTTPHeaderField: "Authorization")
            request.httpMethod = "GET"

            ///Initialize session and task
            //Code for not using completionHandler:
            URLSession.shared.dataTask(with: request) {
                
                //Solution Attempt 1: Version of code for creating "data" variable, where the variable assignment is set to "Data".
                //Got the error message for the above line of code "URLSession.shared.dataTask(with: request) {", saying "Contextual type for closure argument list expects 3 arguments, which cannot be implicitly ignored" with a "Fix" button below, and the text to the left of the "Fix" button being: "Insert '_,_,_ in '".
//                var data = Data
                
                //Solution Attempt 2: Version of code for creating "data" variable, where the variable assignment is set to "Data?".
                //Got the same error message as Solution Attempt 1; Got the error message for the above line of code "URLSession.shared.dataTask(with: request) {", saying "Contextual type for closure argument list expects 3 arguments, which cannot be implicitly ignored" with a "Fix" button below, and the text to the left of the "Fix" button being: "Insert '_,_,_ in '".
//                var data = Data?
                
                do {
                    /// Read data as JSON
                    //The line of code below is where I'm getting the error message: "Cannot find 'data'" in.
                    let json = try JSONSerialization.jsonObject(with: data!, options: [])

                    /// Main dictionary
                    guard let responseDictionary = json as? NSDictionary else {return}

                    //Code for accessing restaraunt detail view info, and assigning it to selectedRestarauntDetailViewInfo view model thats a struct.
                    var selectedVenue = SelectedRestaurantDetailViewInfoNotUsingCodable()
                    
                    
                    //Rest of code for accessing restaraunt detail view info, and assigning it to seelctedRestarauntDetailViewInfo view model thats a struct.
                    selectedVenue.name = responseDictionary.value(forKey: "name") as? String
                    
                    //*Rest of code for getting business info including address, hours, etc..*
                    
                    return selectedVenue
                    
                } catch {
                    print("Caught error")
                }
                }.resume()
    }
}

File2-GetsDataUsingEscapingClosureAndCompletionHandlerForURLSession.shared.dataTaskCode.swift:

import Foundation
import UIKit
import CoreLocation

extension UIViewController {

    func retrieveSelectedRestaurantDetailViewInfo(
        selected_restaurant_business_ID: String,
        completionHandler: @escaping (SelectedRestaurantDetailViewInfoNotUsingCodable?, Error?) -> Void) {
            
            // MARK: Make API Call
            let apiKey = "API key"

            /// Create URL
            let baseURL =
            "https://api.yelp.com/v3/businesses/(selected_restaurant_business_ID)"

            let url = URL(string: baseURL)

            /// Creating Request
            var request = URLRequest(url: url!)
            request.setValue("Bearer (apiKey)", forHTTPHeaderField: "Authorization")
            request.httpMethod = "GET"

            ///Initialize session and task
            //Code for using completionHandler:
            URLSession.shared.dataTask(with: request) { (data, response, error) in

                if let error = error {
                    completionHandler(nil, error)
                }
                
                do {
                    /// Read data as JSON
                    let json = try JSONSerialization.jsonObject(with: data!, options: [])

                    /// Main dictionary
                    guard let responseDictionary = json as? NSDictionary else {return}

                    //Code for accessing restaraunt detail view info, and assigning it to selectedRestarauntDetailViewInfo view model thats a struct.
                    var selectedVenue = SelectedRestaurantDetailViewInfoNotUsingCodable()
                    
                    
                    //Rest of code for accessing restaraunt detail view info, and assigning it to seelctedRestarauntDetailViewInfo view model thats a struct.
                    selectedVenue.name = responseDictionary.value(forKey: "name") as? String
                    
                    //*Rest of code for getting business info including address, hours, etc..*
                    
                    completionHandler(selectedVenue, nil)
                    
                } catch {
                    print("Caught error")
                }
                }.resume()
    }
}

Thanks!

2

Answers


  1. The conversion from the old style dataTask goes from:

    URLSession.shared.dataTask(with: request) { (data, response, error) in
        if let data {
            // do stuff with the non-optional data
        else if let error {
        }
    }.resume()
    

    to:

    let (data, response) = try await URLSession.shared.data(for: request)
    // do stuff with the non-optional data and/or response
    

    There is no longer a task involved that needs to be resumed. The data and response are direct return values (as a tuple).

    This works since you are calling this from inside a function declared as async throws so if the call to data throws and error it will be propagated out to the caller.

    Login or Signup to reply.
  2. In the working version URLSession passes the data object into your closure when it calls it:

    URLSession.shared.dataTask(with: request) { (data, response, error) in
    

    which is why you can access it. This is evident in the method signature

    func dataTask(
        with url: URL,
        completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void
    ) -> URLSessionDataTask
    

    In the async/await world the data is returned directly from the method, but you aren’t referencing the returned data. You need to adapt your code to:

    let (data, response) = try await URLSession.shared.data(with: request)
    

    Then you will be able to use the provided data is the subsequent code as you would have done previously in your closure.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search