Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait For Asynchronous Operation To Complete in Swift

Tags:

I am not sure how to handle this situation as I am very new to iOS development and Swift. I am performing data fetching like so:

func application(application: UIApplication!, performFetchWithCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)!) {     loadShows()     completionHandler(UIBackgroundFetchResult.NewData)     println("Background Fetch Complete") } 

My loadShows() function parses a bunch of data it gets from a website loaded into a UIWebView. The problem is that I have a timer that waits for 10 seconds or so in the loadShows function. This allows for the javascript in the page to fully load before I start parsing the data. My problem is that the completion handler completes before my loadShows() does.

What I would like to do is add a bool for "isCompletedParsingShows" and make the completionHandler line wait to complete until that bool is true. What is the best way to handle this?

like image 786
PretzelJesus Avatar asked Jul 13 '14 16:07

PretzelJesus


People also ask

What is async await in Swift?

An asynchronous function in Swift can give up the thread that it's running on, which lets another asynchronous function run on that thread while the first function is blocked. When an asynchronous function resumes, Swift doesn't make any guarantee about which thread that function will run on.

How do I add a delay in Swift?

To add a delay to your code we need to use GCD . GCD has a built in method called asyncAfter , which will allow us to run code after a given amount of time. In the above code, Before delay will be printed out first and after 2 seconds, Async after 2 seconds will be printed.


2 Answers

you have to pass your async function the handler to call later on:

func application(application: UIApplication!, performFetchWithCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)!) {     loadShows(completionHandler) }  func loadShows(completionHandler: ((UIBackgroundFetchResult) -> Void)!) {     //....     //DO IT     //....      completionHandler(UIBackgroundFetchResult.NewData)     println("Background Fetch Complete") } 

OR (cleaner way IMHO)

add an intermediate completionHandler

func application(application: UIApplication!, performFetchWithCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)!) {     loadShows() {         completionHandler(UIBackgroundFetchResult.NewData)         println("Background Fetch Complete")     } }  func loadShows(completionHandler: (() -> Void)!) {     //....     //DO IT     //....     completionHandler() } 
like image 163
Daij-Djan Avatar answered Sep 19 '22 13:09

Daij-Djan


two ways to solve this, both use Grand Central Dispatch (which is similar in Swift and Objective C):

  1. change loadShows method to make it synchronous and use the same dispatch queue as completionHandler, then wrap the entire body of the method in a dispatch_async ; this way the method call ends right away, but the completionHandler will be called after loadShows if finished, just like in a synchronous program

  2. use a GCD semaphore - just like the BOOL you mention, but created with dispatch_semaphore_create ; you call dispatch_semaphore_wait before completionHandler to make it wait for the semaphore to be unlocked (unlock it with dispatch_semaphore_signal ) ; remember to place your method body inside a dispatch_async call in order not to have it block the rest of the app while waiting for loadShows to complete.

like image 21
Alex Avatar answered Sep 23 '22 13:09

Alex