1
votes

My program pulls from an API to return appointment data into a widget. I set up a closure to handle pulling an unknown number of pages from the API. Each time loadResults is called, it queries the API, starting with page 1, and then checks to see if there are any pages left and calls itself again if any pages are remaining to pull. Once all pages of the API are pulled and loaded into my array, it stops running.

I'm loading the getAllResults function in my getTimeline function and would like to wait until it's finished running to then load my timeline Entry.

Is there a way to put an optional completion handler on getAllResults so I can use this function to then load my timeline entry once the completion handler finishes? Since I'm calling it multiple times I'd like to let it run recursively until finished and be able to alert the timeline it's finished and ready to load.

Current code:

func getAllResults(){
    loadResults { (pageReturned) in
        if pageReturned == Int(totalPages)! {
            pages = 1
            parseData(apptReturn: apptList)
            //end the call
        } else if pageReturned < Int(totalPages)! {
            pages = pages + 1
            getAllResults()
               
            }

        }
    }

I was thinking something like this to name the completion handler and call it within the nested closure (but this doesn't work). Is there a way to reference the getAllResults completion within the loadResults closure?

func getAllResults(completedResults completion: @escaping (Bool) -> ()){
    loadResults { (pageReturned) in
        if pageReturned == Int(totalPages)! {
            pages = 1
            parseTodayData(apptReturn: apptList)
            getAllResults(completedResults: true) //error: Cannot convert value of type 'Bool' to expected argument type '(Bool) -> ()'

            //end the call
        } else if pageReturned < Int(totalPages)! {

            pages = pages + 1
            getAllResults(completedResults: false). //error: Cannot convert value of type 'Bool' to expected argument type '(Bool) -> ()'
                //
            }
                //
            }

        }
1
Don't you need to pass something to loadResults so it would load the next page of results? Otherwise, it would keep returning the same set of results with each recursive call - New Dev
No. I have the API default to checking page 1 of the API call. It returns the API results which include the number of pages to the response (totalPages). I then add 1 to the page I just checked and then if there are remaining pages, it calls those as well one by one. - LearningSwift
Also, within the loadResults function (which I didn't show the details of here) it adds each API JSON return to an array. - LearningSwift

1 Answers

2
votes

Regardless of whether you need recursion or not, for an async call you'd need a completion handler, so that the caller can know when the operation has completed.

If you want to use recursion, you can pass the completion handler down to the next recursive invocation, and call it when completed.

func getResults(onCompleted handler: @escaping () -> Void) {
   loadResults { pageReturned in
      let needToLoadMore = ... // determine if you need to load more pages
      // other side effects
      
      if needToLoadMore {
         getResults(onCompleted: handler)
      } else {
         handler()
      }
   }
}