0

I'm trying to make a second API endpoint request, and to do so, am making a second URLSession.shared.dataTask within the initial/first API endpoint request’s URLSession.shared.dataTask’s do block. However, my code doesn't execute after/within the second API endpoint request's URLSession.shared.dataTask’s line of code/scope.

I keep getting an infinite while loop that executes outside the scope of the second API endpoint request's URLSession.shared.dataTask when I run the program.

I’m using the Yelp Fusion API’s “Search” endpoint here. Documentation: https://www.yelp.com/developers/documentation/v3/business_search

The tutorial that I got the original code and format from: https://medium.com/@khansaryan/yelp-fusion-api-integration-af50dd186a6e

Code:

Venue.swift:

import Foundation

struct Venue {
    var name: String?
    var id: String?
    var rating: Float?
}

FetchData.swift:

import Foundation

extension ViewController {
    
    func retrieveVenues(latitude: Double,
                        longitude: Double,
                        category: String,
                        limit: Int,
                        sortBy: String,
                        completionHandler: @escaping ([Venue]?, Error?) -> Void) {

        //Prints
        print("Check 1")

        //Making API Call
        let apikey =
        "API key"

        let baseURL =
        "https://api.yelp.com/v3/businesses/search?latitude=\(latitude)&longitude=\(longitude)&categories=\(category)&limit=\(limit)&sort_by=\(sortBy)"

        let url = URL(string: baseURL)

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

        //Prints before "boringssl_metrics_log_metric_block_invoke" log statement.
        print("Check 2")

        //Log statement "boringssl_metrics_log_metric_block_invoke" printed after the below line of code.

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

            //Prints after "boringssl_metrics_log_metric_block_invoke" log statement.
            print("Check 3")

            if let error = error {
                completionHandler(nil, error)

               //Doesn't print. Is set to print after "boringssl_metrics_log_metric_block_invoke" log statement.
               print("Check 4")

            }
            
            //Prints after "boringssl_metrics_log_metric_block_invoke" log statement.
            print("Check 5")
            
            do {

                //Prints.
                print("Check 6")

                // Read data as JSON
                let json = try JSONSerialization.jsonObject(with: data!, options: [])

                //Prints.
                print("Check 7")

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

                //Prints.
                print("Check 8")

                guard let totalBusinesses = resp.value(forKey: "total") as? Int else {return}
                
                //Prints.
                print("totalBusinesses outisde and after guard-let statment:", totalBusinesses)

                
                // Businesses
                guard let businesses = resp.value(forKey: "businesses") as? [NSDictionary] else {return}

                //Prints.
                print("Check 9")

                var venuesList: [Venues] = []

                //Prints.
                print("Check 10")              

                //Accessing each business
                for business in businesses {
                    var venue = Venues()
                    venue.name = business.value(forKey: "name") as? String
                    venue.id = business.value(forKey: "id") as? String
                    venue.rating = business.value(forKey: "rating") as? Float
                     
                    venuesList.append(venue)

                    //Prints.
                    print("venuesList.count inside the first initial API Search endpoint request:", venuesList.count)

                }
                
                //Prints.
                print("venuesList.count outside the first initial API Search endpoint request, and its for-loop: for business in businesses { and before the while loop for extra needed API Search endpoint requests below:", venuesList.count)


                //Code for making the amount of API requests to show and add all businesses to venuesList using limit and offsset pararmeters, and totalBusinesses variable. Limit is always 50, and offsset parameter as of now is also always 50, and will be incrimented by 50 at then end of the while loop's executing code's body (within the while loop).

                //Code for an if-statement if the total number of businesses from the initial API Search enpdoint request is more than 50, and therefore, need to make more API "Search" endpoint requests.
                if totalBusinesses > 50 {

                    //Code for making more requests.

                    //Offset value counter. Will add a 50 at the end of every while loop iteration (within evey while loop iteration.)
                    var offsetValue = 50

                    //Print check for offsetValue before while loop for any extra needed requests. Should just print 50. Prints 50. 
                    print("offsetValue before while loop for any extra needed API Search endpoint requests:", offsetValue)

                    //Print Check for seeing what venuesList.count is before the while loop below for any extra needed requests. Prints.
                    print("venuesList.count before while loop for any extra needed API Search endpoint requests:", venuesList.count)

                    //While loop for making requests and adding venue to VeneusList until the total number of businesses have been added.
                    while venuesList.count != totalBusinesses {

                        let baseURL =
                        "https://api.yelp.com/v3/businesses/search?latitude=\(latitude)&longitude=\(longitude)&categories=\(category)&limit=\(limit)&offset=\(offsetValue)&sort_by=\(sortBy)"
                        
                        let url = URL(string: baseURL)

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

                        //Prints, however, the "boringssl_metrics_log_metric_block_invoke" log statement isn't printed after it, when it should be. Is described more below.
                        print("Check 2")

                        //Log statement "boringssl_metrics_log_metric_block_invoke" is not printed after the below line of code.

                        //Print statements aren't being printed after the below line of code starting with "URLSession.shared.dataTask".
                        //Initialize session and task
                        URLSession.shared.dataTask(with: request) { (data, response, error) in

                            //Print check after below code doesnt print, therefore code under it isn't being executed.

                            //Doesn't print.
                            print("Check 3")

                            if let error = error {
                                completionHandler(nil, error)

                                //Doesn't print.
                                print("Check 4")
                            }
                            //Doesn't print.
                            print("Check 5")

                            do {

                                //Doesn't print.
                                print("Check 6")

                                // Read data as JSON
                                let json = try JSONSerialization.jsonObject(with: data!, options: [])

                                //Doesn't print.
                                print("Check 7")

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

                                //Doesn't print.
                                print("Check 8")

                                // Businesses
                                guard let businesses = resp.value(forKey: "businesses") as? [NSDictionary] else {return}

                                
                                //Print Check for businesses at start of first needed extra API Endpoint Request with offset of 50. Doesn't print.
                                print("Businesses at start of first needed extra API Endpoint Request with offset of 50:", businesses)

                                //Doesn't print.
                                print("Check 9")

                                //Accessing each business
                                for business in businesses {
                                    var venue = Venues()
                                    venue.name = business.value(forKey: "name") as? String
                                    venue.id = business.value(forKey: "id") as? String
                                    venue.rating = business.value(forKey: "rating") as? Float
                                     
                                    venuesList.append(venue)
                                }
                                

                            } catch {
                                print("Caught error")
                            }
                            }.resume()

                        offsetValue += 50

                        //Prints.
                        print("offsetValue after its incrimented by 50 at end of and still within while-loop:", offsetValue)
                        
                        //Prints.
                        print("venuesList.count after offsetValue print statement where its incrimented by 50 at the end of and still within while-loop:",  venuesList.count)


                    }
                    //While Loop closing bracket is one line above this comment.

                    //Print check for exitting while loop.
                    //Still isn't being printed yet, because am stuck in an infinite while loop.
                    print("Exited while loop for any needed extra API endpoint requests.")
                }
                //closing bracket of if-statement: "if totalBusinesses > 50 {" is one line above this comment.
                
                completionHandler(venuesList, nil)
                
            } catch {
                print("Caught error")
            }
            }.resume()

    }
}

Current Return Statement in Terminal:

Check 1
Check 2
[Date and time when project is run and project name] [boringssl] boringssl_metrics_log_metric_block_invoke(153) Failed to log metrics
Check 3
Check 5
Check 6
Check 7
Check 8
totalBusinesses outisde and after guard-let statment: 103
Check 9
Check 10
venuesList.count inside the first initial API Search endpoint request: 1
venuesList.count inside the first initial API Search endpoint request: 2
...
venuesList.count inside the first initial API Search endpoint request: 49
venuesList.count inside the first initial API Search endpoint request: 50
venuesList.count outside the first initial API Search endpoint request, and its for-loop: for business in business { and before the while loop for extra needed API Search endpoint requests below: 50
offsetValue before while loop for extra needed requests: 50
venuesList.count before while loop for any extra needed API Search endpoint requests: 50
Check 2
offsetValue before while loop for extra needed requests: 100
venuesList.count before while loop for any extra needed API Search endpoint requests: 50
Check 2
offsetValue before while loop for extra needed requests: 150
venuesList.count before while loop for any extra needed API Search endpoint requests: 50
Check 2
offsetValue before while loop for extra needed requests: 200
venuesList.count before while loop for any extra needed API Search endpoint requests: 50
Check 2
Continues in an infinite while loop until quitting program (closing or stopping simulator).

Thanks!

--

Update:

Below is an updated FetchData.swift version using @Paulw11's solution (Doesn't include the async/await code yet, because I want to figure out how to return the value (the variable totalBusinesses's value) totalBusinesses from the makeInitialAPIRequest function that also contains/sends back a completion handler, to the retrieveVenues function, first. This is a current sticking point. Thanks for the help!):

Updated FetchData.swift version using @Paulw11's solution without async/await code:

import Foundation

extension ViewController {
    
    //Below code is actually located at top of ViewController class.
    var outerScopeRunningVenuesList: [Venue] = []
    
    func retrieveVenues(latitude: Double,
                        longitude: Double,
                        category: String,
                        limit: Int,
                        sortBy: String,
                        completionHandler: @escaping ([Venue]?, Error?) -> Void) {

        //Code for making first/Intial API request, and using outerScopeRunningVenuesList for venuesList values.
        makeInitialAPIRequest(latitude: latitude,
                              longitude: longitude,
                              category: category,
                              limit: limit,
                              sortBy: sortBy) { (response, error) in
        
            if let response = response {
                
                self.outerScopeRunningVenuesList = response
                
                //*Still need to handle the error here, do later.

            }
        }
        
        //Code for getting totalBusinesses return value from makeInitialAPIRequest function.
        var overallMakeInitialAPIRequestReturnValue = makeInitialAPIRequest(latitude: latitude,
                                                                     longitude: longitude,
                                                                     category: category,
                                                                     limit: limit,
                                                                     sortBy: sortBy) { (response, error) in
                                               
                                                   if let response = response {
                                                       
                                                       self.outerScopeRunningVenuesList = response
                                                       
                                                       //*Still need to handle the error here, do later.

                                                   }
                                               }
        
        
        //Getting totalBusinesses return value.
        var recievedTotalBusinesses = overallMakeInitialAPIRequestReturnValue.0
        

        //Code for making the amount of API requests to show and add all businesses to venuesList using limit and offsset pararmeters, and totalBusinesses variable. Limit is always 50, and offsset parameter as of now is also always 50, and will be incrimented by 50 at then end of the while loop's executing code's body (within the while loop).

        //Code for an if-statement if the total number of businesses from the initial API Search enpdoint request is more than 50, and therefore, need to make more API "Search" endpoint requests.
        if recievedTotalBusinesses > 50 {

            //Code for making more requests.

            //Offset value counter. Will add a 50 at the end of every while loop iteration (within evey while loop iteration.)
            var offsetValue = 50

            //Print check for offsetValue before while loop for any extra needed requests. Should just print 50.
            print("offsetValue before while loop for extra needed requests:", offsetValue)

            //Print Check for seeing what venuesList.count is before the while loop below.
            print("outerScopeRunningVenuesList.count before while loop for any extra needed API Search endpoint requests:", outerScopeRunningVenuesList.count)

            //While loop for making requests and adding venue to VeneusList until the total number of businesses have been added.
            while outerScopeRunningVenuesList.count != recievedTotalBusinesses {

                //Code for making extra needed API requests, and using outerScopeRunningVenuesList for venuesList values.
                makeAnyExtraNeededAPIRequest(venuesList: outerScopeRunningVenuesList,
                                             offsetValue: offsetValue,
                                             latitude: latitude,
                                             longitude: longitude,
                                             category: category,
                                             limit: limit,
                                             sortBy: sortBy) { (response, error) in
                
                    if let response = response {
                        
                        self.outerScopeRunningVenuesList = response

                        //*Still need to handle the error here, do later.
                    }


                }

                offsetValue += 50

                print("offsetValue after its incrimented by 50 at end of and still within while-loop:", offsetValue)

                print("outerScopeRunningVenuesList.count after offsetValue print statement where its incrimented by 50 at the end of and still within while-loop:",  outerScopeRunningVenuesList.count)


            }
            //While Loop closing bracket is one line above this comment.

            //Print check for exitting while loop.
            //Still isn't being printed yet, because am stuck in an infinite while loop.
            print("Exitted while loop for any needed extra API Endpoint requests.")
        }
        //Closing bracket of if-statement: "if totalBusinesses > 50 {" is one line above this comment.
                
        completionHandler(outerScopeRunningVenuesList, nil)

    }
    
    func makeInitialAPIRequest(latitude: Double,
                               longitude: Double,
                               category: String,
                               limit: Int,
                               sortBy: String,
                               completionHandler: @escaping ([Venue]?, Error?) -> Void) -> (totalBusinesses: Int) {
        
        print("Check 1")
        
        //Making API Call
        let apikey =
        "API key"

        let baseURL =
        "https://api.yelp.com/v3/businesses/search?latitude=\(latitude)&longitude=\(longitude)&categories=\(category)&limit=\(limit)&sort_by=\(sortBy)"

        let url = URL(string: baseURL)

        // Creating Request
        var request = URLRequest(url: url!)
        request.setValue("Bearer \(apikey)", forHTTPHeaderField: "Authorization")
        request.httpMethod = "GET"
        
        print("Check 2")

        //Initialize session and task
        URLSession.shared.dataTask(with: request) { (data, response, error) in
            
            print("Check 3")

            if let error = error {
                completionHandler(nil, error)
                
                print("Check 4")

            }
            
            print("Check 5")
            
            do {
                
                print("Check 6")

                // Read data as JSON
                let json = try JSONSerialization.jsonObject(with: data!, options: [])
                
                print("Check 7")

                // Main dictionary
                guard let resp = json as? NSDictionary else {return}
                
                print("Check 8.1: Before totalBusinesses.")

                guard let totalBusinesses = resp.value(forKey: "total") as? Int else {return}
                
                print("Check 8.2: After totalBusinesses and before businesses.")

                
                // Businesses
                guard let businesses = resp.value(forKey: "businesses") as? [NSDictionary] else {return}
                
                print("Check 9")

                var venuesList: [Venues] = []

                
                //Accessing each business
                for business in businesses {
                    var venue = Venues()
                    venue.name = business.value(forKey: "name") as? String
                    venue.id = business.value(forKey: "id") as? String
                    venue.rating = business.value(forKey: "rating") as? Float
                     
                    venuesList.append(venue)
                }
                
                completionHandler(venuesList, nil)
                return totalBusinesses
                
            } catch {
                print("Caught error")
            }
            }.resume()
        
    }
    
    func makeAnyExtraNeededAPIRequests(veneusList: [Venue]?,
        offsetValue: Int,
        latitude: Double,
        longitude: Double,
        category: String,
        limit: Int,
        sortBy: String,
        completionHandler: @escaping ([Venue]?, Error?) -> Void)  {
        
        print("Check 1")
        
        //Code for making any needed extra API endpoint requests.
        let baseURL =
        "https://api.yelp.com/v3/businesses/search?latitude=\(latitude)&longitude=\(longitude)&categories=\(category)&limit=\(limit)&offset=\(offsetValue)&sort_by=\(sortBy)"
        
        let url = URL(string: baseURL)

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

        print("Check 2")

        //Print statements arent being printed after below line of code starting with "URLSession.shared.dataTask".
        //Initialize session and task
        URLSession.shared.dataTask(with: request) { (data, response, error) in
            
            print("Check 3")

            if let error = error {
                completionHandler(nil, error)

                
                print("Check 4")
            }
            
            print("Check 5")

            do {

                
                print("Check 6")

                // Read data as JSON
                let json = try JSONSerialization.jsonObject(with: data!, options: [])

                
                print("Check 7")

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

                
                print("Check 8")

                // Businesses
                guard let businesses = resp.value(forKey: "businesses") as? [NSDictionary] else {return}

                
                //Print Check for businesses at needed extra API Endpoint Request.
                print("Businesses at needed extra API Endpoint Request:", businesses)

                
                print("Check 9")

                //Accessing each business
                for business in businesses {
                    var venue = Venues()
                    venue.name = business.value(forKey: "name") as? String
                    venue.id = business.value(forKey: "id") as? String
                    venue.rating = business.value(forKey: "rating") as? Float
                     
                    venuesList.append(venue)
                }
                
                
                completionHandler(venuesList, nil)

            } catch {
                print("Caught error")
            }
            }.resume()
}
coder
  • 19
  • 4
  • 1
    _"Prints before borings metircs error"_, what is "borings metircs" and do you have an error here that you can share? – Joakim Danielson May 19 '22 at 17:19
  • Unrelated BUT: Using Codable should simplify your decoding code. Avoid using `NSDictionary` in Swift 3+, prefers: `[String: Any]` (since it's JSON). What do you do once you get the `completionHandler`? If you quit your `ViewController` instance, then it might explain that... – Larme May 19 '22 at 18:08
  • @JoakimDanielson My apologies, I misspelled that; should've been "borings metrics". That refers to this log statement: "[boringssl] boringssl_metrics_log_metric_block_invoke(153) Failed to log metrics" that is printed in the terminal after "Check 2" and before "Check 3" in the initial API endpoint request's code. I had the print statements being used here in the same position as the initial API endpoint request, and will include those in the post. I had that comment there in the initial API endpoint request code because I had thought that the log statement was causing a problem, but – coder May 19 '22 at 18:10
  • @JoakimDanielson continued: but found that it was being printed whenever an API was being accessed (in my case), and wasn't causing the problem at the time, and was suggested by others online that it could be ignored. For clarification, the log statement isn't being printed after/within the second API endpoint request's URLSession.shared.dataTask line of code/scope. I'll update the post with print statements I'm getting from the terminal. – coder May 19 '22 at 18:16
  • @Larme Thank you, I've been told this regarding codable recently, and will switch to that in the near future. Understood regarding ```NSDictionary``` and prefers ```[String: Any]```, I'll change that soon. Once I get the completion handler, I send it back to the ```ViewController``` via an escaping closure so ```venuesList``` in the form of ```[Venues]?``` can be used (in the ```ViewController```). – coder May 19 '22 at 18:30
  • To me it looks like you are spamming the server with your while loop so maybe the server reacts to that in some way. I would temporary remove the loop so only one request is made and see if you get a response then. – Joakim Danielson May 19 '22 at 19:55
  • Your problem is that your while loop is outside the second network operation, which is also going to complete asynchronously. This means you are going to request the third 50 records before you have the second 50 records and so on. Your while loop termination depends on an asynchronous value. You should restructure your code so that you don't duplicate the network request (Your current code should trigger your DRY spider sense). – Paulw11 May 19 '22 at 20:16
  • Split your function in two. Take the network operation and put it in its own function. Have that function accept an optional `offset`. You then have `retrieveVenues` call that as many times as needed using a loop. `retrieveVenues` will need to dispatch the loop asynchronously so as not to block the caller and then block waiting for the result each time. What iOS version is your minimum? Can you use async/await? – Paulw11 May 19 '22 at 20:21
  • @JoakimDanielson Thank you. I tried this, and instead of using a while loop, used an if-statement ("if totalBusinesses > 50 {" (*Yes, a second if-statement of the same if-statement before it)) since one API request was already working before this (not making any extra needed API requests if the totalBusinesses was greater than 50). The venuesList.count still wasn't updated to anything greater than 50, but the program ran correctly, and showed info for only the 50 first businesses. – coder May 19 '22 at 21:00
  • @Paulw11 Thank you, this makes sense. I'll do this when I get back (have to go to work now), and report back tomorrow. Much thanks! – coder May 19 '22 at 21:02
  • @Paulw11 Apologies, completely forgot to reply to the last part of your last comment. My minimum is iOS 15.0, and yes, I can use async/await. I haven't used it many times, but will do more research before implimenting. – coder May 20 '22 at 22:37
  • @Paulw11 I don't understand why my current code example would request the third 50 records before I have the second 50 records (and so on). Is this because requesting data (the URLSession.shared.dataTask) takes a few seconds for it to be returned, and meanwhile, does the while loop outside of that request continue? I'm new to this, and this info is very helpful. – coder May 20 '22 at 22:51
  • Yes. Network requests are asynchronous. Even if they take half a second, the result is returned to the completion handler closure. The next statement after `resume` executes immediately. It doesn't block. That is what async/await is much nicer. It lets you code linearly even if there is an asynchronous task – Paulw11 May 21 '22 at 05:31
  • @Paulw11 When implementing your suggested solution, I'm having trouble sending a return value for totalBusinesses back from the makeInitialAPIRequest function to the retrieveVenues function (after the completionHandler call), which seems to be because I'm using a completionHandler in the makeInitialAPIRequest function. How would I do this? I've included code of an updated version of my FetchData.swift file using your advice to the post, it just doesn't contain the async/await code in there yet, as I'm trying to make sure this is set up "correctly" without it first. Thanks for all the help. – coder May 21 '22 at 06:01

1 Answers1

0

Since you are targeting iOS 15, you are making things much harder for yourself by not embracing async/await. You can also use Codable to handle the JSON parsing.

First, create Codable structs to handle the result (hint app.quicktype.io can do this for you):

// MARK: - BusinessSearchResult
struct BusinessSearchResult: Codable {
    let total: Int
    let businesses: [Business]
    let region: Region
}

// MARK: - Business
struct Business: Codable {
    let rating: Double
    let price, phone, alias: String?
    let id: String
    let isClosed: Bool?
    let categories: [Category]
    let reviewCount: Int?
    let name: String
    let url: String?
    let coordinates: Center
    let imageURL: String?
    let location: Location
    let distance: Double
    let transactions: [String]

    enum CodingKeys: String, CodingKey {
        case rating, price, phone, id, alias
        case isClosed
        case categories
        case reviewCount
        case name, url, coordinates
        case imageURL
        case location, distance, transactions
    }
}

// MARK: - Category
struct Category: Codable {
    let alias, title: String
}

// MARK: - Center
struct Center: Codable {
    let latitude, longitude: Double
}

// MARK: - Location
struct Location: Codable {
    let city, country, address2, address3: String?
    let state, address1, zipCode: String?

    enum CodingKeys: String, CodingKey {
        case city, country, address2, address3, state, address1
        case zipCode
    }
}

// MARK: - Region
struct Region: Codable {
    let center: Center
}

Then you can create an api class that uses async/await to fetch the data.

The basic strategy is:

  • Fetch the first results
  • Take a note of the total results that are expected
  • Limit the total to 1000 (This is an API limit)
  • Keep making requests, increasing the offset each time, until you have the expected results.
  • Return the results
class YelpApi {
    
    enum SortOption: String {
        case bestMatch="best_match"
        case rating="rating"
        case reviewCount="review_count"
        case distance="distance"
    }
    
    private var apiKey: String
    
    init(apiKey: String) {
        self.apiKey = apiKey
    }
    
    func searchBusiness(latitude: Double,
                        longitude: Double,
                        category: String? = nil,
                        sortBy: SortOption? = nil) async throws -> [Business] {
        
        var queryItems = [URLQueryItem]()
        queryItems.append(URLQueryItem(name:"latitude",value:"\(latitude)"))
        queryItems.append(URLQueryItem(name:"longitude",value:"\(longitude)"))
        if let category = category {
            queryItems.append(URLQueryItem(name:"categories", value:category))
        }
        if let sortOption = sortBy {
            queryItems.append(URLQueryItem(name:"sort_by",value:sortOption.rawValue))
        }
        
        var results = [Business]()
        
        var expectedCount = 0
        let countLimit = 50
        var offset = 0
        
        queryItems.append(URLQueryItem(name:"limit", value:"\(countLimit)"))
        
        repeat {
            
            var offsetQueryItems = queryItems
            
            offsetQueryItems.append(URLQueryItem(name:"offset",value: "\(offset)"))
            
            var urlComponents = URLComponents(string: "https://api.yelp.com/v3/businesses/search")
            urlComponents?.queryItems = offsetQueryItems
            
            guard let url = urlComponents?.url else {
                throw URLError(.badURL)
            }
            
            var request = URLRequest(url: url)
            request.setValue("Bearer \(self.apiKey)", forHTTPHeaderField: "Authorization")
            
            let (data, _) = try await URLSession.shared.data(for: request)
            let businessResults = try JSONDecoder().decode(BusinessSearchResult.self, from:data)

            expectedCount = min(businessResults.total,1000)
            
            results.append(contentsOf: businessResults.businesses)
            offset += businessResults.businesses.count
        } while (results.count < expectedCount)
        
        return results
    }
}

I have used URLComponents rather than string interpolation as this handles things like percent encoding where required.

Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • Wow, thank you! This is a big help. Also, thank you for the link. Question: does ```category``` after ```value:``` in ```queryItems.append(URLQueryItem(name:"categories", value:category))``` signify the string to be used for the type of category’s identifier defined in the Yelp Fusion API documentation to be returned from the search? For example, if I wanted to return the category of gyms using the identifier “gyms” from the Yelp Fusion API documentation, would “gyms” be in the place where ```category``` currently is, and replace it? Thanks for the help! – coder May 22 '22 at 02:06
  • Yes, you can find the information the in api document you linked to. If you pass a string for the `category` argument then it will be added to the url – Paulw11 May 22 '22 at 03:49
  • Thank you, this makes sense, appreciate it! – coder May 22 '22 at 05:37
  • One question; how would I call this YelpApi class in my ViewController? I have the following code, but am getting problems. I've also looked up documentation about Task and still having trouble. – coder May 27 '22 at 02:20
  • I'll actually make a new question post referencing this post. Thanks for the help! – coder May 27 '22 at 02:53