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
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.
xCode 9.2, Swift 4
class AsyncOperation {
private let semaphore: DispatchSemaphore
private let dispatchQueue: DispatchQueue
typealias CompleteClosure = ()->()
init(numberOfSimultaneousActions: Int, dispatchQueueLabel: String) {
semaphore = DispatchSemaphore(value: numberOfSimultaneousActions)
dispatchQueue = DispatchQueue(label: dispatchQueueLabel)
}
func run(closure: @escaping (@escaping CompleteClosure)->()) {
dispatchQueue.async {
self.semaphore.wait()
closure {
self.semaphore.signal()
}
}
}
}
let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 1, dispatchQueueLabel: "AnyString")
asyncOperation.run { completeClosure in
// sync/async action
// ...
// action complete
completeClosure()
}
import UIKit
class ViewController: UIViewController {
let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 1, dispatchQueueLabel: "AnyString")
var counter = 1
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton(frame: CGRect(x: 50, y: 50, width: 100, height: 40))
button.setTitle("Button", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
view.addSubview(button)
}
@objc func buttonTapped() {
print("Button tapped at: \(Date())")
asyncOperation.run { completeClosure in
let counter = self.counter
print(" - Action \(counter) strat at \(Date())")
self.counter += 1
DispatchQueue.global(qos: .background).async {
sleep(1)
print(" - Action \(counter) end at \(Date())")
completeClosure()
}
}
}
}