ios – Why isn’t the count of an array of classes becoming greater than 50 when using Yelp Fusion API? Related to a second URLSession.shared.dataTask Swift

I’m trying to add classes that contain business data of a business to a list (array) that hold these classes when the limit of the total number of businesses for an initial API “Search” endpoint request using the Yelp Fusion API is greater than greater 50, where I use the limit of 50 in the initial request, and any additional needed requests (if the total amount of businesses returned is over 50), and for also for any additional needed requests, create an offset of 50 until the total number of business’s information has been added to the mentioned list (array) of classes (one class for each business) through using a while loop until I get all of the information I want from all businesses.

However, the list (array) of classes isn’t going over the count of 50 in my below code. Yelp Fusion API has a maximum limit of 50 for this “Search” API endpoint request, which I’m using, but it shouldn’t affect this separate array’s count of classes.

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

List of reason’s why I think this problem is happening: Reason #1 (and main reason):

The code after the second API endpoint request’s URLSessionURLSession.shared.dataTask’s line of code (within that shared data task’s scope/inide its closing bracket’s parentheses) isn’t being executed, however code after its closing bracket and the “.resume()” code is being executed; Including incrementing the offsetValue, and the print statement showing the offsetValue’s current value both being at the end of the while loop’s executing code block. I know this through using print statements. More specifically, print statements after the second URLSessionURLSession.shared.dataTask line of code (within its do block) aren’t printing.

More info on this reason: I’m trying to make a second API endpoint request, and to do so, am making a second URLSessionURLSession.shared.dataTask within the initial/first API endpoint request, and within its URLSessionURLSession.shared.dataTask closing bracket.

Reason #2:

I saw here https://developer.apple.com/forums/thread/71636 that when using the NSURLSession convenience APIs with a completion handler closure on the shared session, the completion handler is called on some arbitrary background thread, and that if I want to access the UI, I’d have to bounce to the main thread. I’m not using a completionHandler here in this second URLSessionRequest’s scope within its closing bracket, but the issue of it being called on some arbitrary background thread could be related to my problem.

Reason #3:

Me not calling a completion handler at the end of the do block within the second URLSession.shared.dataTask’s brackets. Is saw from the documentation here: https://developer.apple.com/documentation/foundation/urlsession that “func dataTask(with: URLRequest, completionHandler: (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask (specific documentation link for this: https://developer.apple.com/documentation/foundation/urlsession/1407613-datask) needs a completionHandler called to end the task. Note, however, that the tutorial doesn’t include a “completionHandler:” argument in the initial API “Search” endpoint requests’s URLSesion.shared.dataTask parameter. I tried including the completionHandler at the appropriate position here, to see if the code after /within the second URLSesion.shared.dataTask line of code will work, however it didn’t, and I still got an infinite loop. Note: I don’t want to use this completionHandler here anyway; I want to use it after all extra needed API “Search” endpoint requests are made, so it exits the function all of this code is in with the wanted venuesList content. From the above same documentation (specific documentation link for this: https://developer.apple.com/documentation/foundation/urlsession/1410592-datask), however, this did not work, and I got the error message: “Contextual type for closure argument list expects 3 arguments, which cannot be implicitly ignored”,a and gives a ‘fix’ option of ‘Insert ‘,,_ in ‘”, which confirmed my original thought that I needed these three arguments of “(Data?, URLResponse?, Error?”)” to accomplish what I’m trying to do with the data returned by the server.

I’m not getting any error messages (besides the one described in the above paragraph, of which’s way I’m no longer using). I am getting an infinite while loop though, with print messages at the end of the executing code inside the while loop continuously being printed, which seems to be because the code after the second URLSession.shared.dataTask line of code (within its do block , and within its overall scope and closing bracket) isn’t being executed.

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

Minimum code examples:

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) {

        //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"

        //Initialize session and task
        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 resp = json as? NSDictionary else {return}

                guard let totalBusinesses = resp.value(forKey: "total") as? Int else {return}

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

                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)
                }

                //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 offsetValueForNextSaturday201AMDateTime before while loop for any extra needed requests for nextSaturday201AMDateTime. 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("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 before borings metircs error.
                        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 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.
                                print("Businesses at start of first needed extra API Endpoint Request with offset of 50:", businesses)

                                //Print check.
                                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)
                                }
                                
                                //Commented out below line of code, because still didn't work. Note: Don't want to use this completionHandler here anyway, want to use it after all extra needed API "Search" endpoint requests are made, so it exits the function all of this code is in with the wanted venuesList content.
                                //completionHandler(venuesList, nil)

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

                        offsetValue += 50

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

                        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("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(venuesList, nil)
                
            } catch {
                print("Caught error")
            }
            }.resume()

    }
}

Thanks!

Leave a Comment