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?
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.
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.
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") }
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() }
two ways to solve this, both use Grand Central Dispatch (which is similar in Swift and Objective C):
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With