Wait For Asynchronous Operation To Complete in Swift

后端 未结 3 1204
温柔的废话
温柔的废话 2020-12-01 00:53

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         


        
相关标签:
3条回答
  • 2020-12-01 01:24

    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()
    }
    
    0 讨论(0)
  • 2020-12-01 01:28

    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.

    0 讨论(0)
  • 2020-12-01 01:41

    Details

    xCode 9.2, Swift 4

    Solution

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

    Usage

    let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 1, dispatchQueueLabel: "AnyString")
    asyncOperation.run { completeClosure in
        // sync/async action
        // ...
    
    
        // action complete        
        completeClosure()
    }
    

    Full sample

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

    Results

    0 讨论(0)
提交回复
热议问题