5

I'm working on an ios app where in my appDelegate I have:

func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {    
    self.api.signInWithToken(emailstring, token: authtokenstring) {
        (object: AnyObject?, error:String?) in            
            if(object != nil){
                self.user = object as? User
                // go straight to the home view if auth succeeded
                var rootViewController = self.window!.rootViewController as UINavigationController
                let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
                var homeViewController = mainStoryboard.instantiateViewControllerWithIdentifier("HomeViewController") as HomeViewControllerenter
                // code here
                rootViewController.pushViewController(homeViewController, animated: true)
            }
        }
    return true
}

The api.signInWithToken is an asynchronous call made with Alamofire, and I would like to wait for it's completion before returning true at the end end of func application.

Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
brian S
  • 61
  • 1
  • 1
  • 4
  • 2
    Swift doesn't have this kind of functionality. You'll want to set up your window and root view controller in the body of `didFinishLaunching`, perhaps with a loading graphic, and then navigate to a new view controller in the API sign-in completion handler. – Nate Cook Nov 06 '14 at 20:10
  • didFinishLaunching is deprecated, but that sounds like a good idea – brian S Nov 07 '14 at 21:51

2 Answers2

11

Note: You should not do it this way, as it blocks the thread. See Nate's comment above for a better way.

There is a way to wait for an async call to complete using GCD. The code would look like the following

var semaphore = dispatch_semaphore_create(0)

performSomeAsyncTask {
    ...
    dispatch_semaphore_signal(semaphore)
}

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
dispatch_release(semaphore)

Wikipedia has an OK article in case you know nothing about semaphores.

Robin Daugherty
  • 7,115
  • 4
  • 45
  • 59
Anton
  • 2,342
  • 15
  • 17
  • I tried the above; dispatch_release(semaphore) has been removed by Apple. – brian S Nov 07 '14 at 21:52
  • 2
    So I removed dispatch_release(semaphore),but the semaphore_wait seems to block the whole thread. Looks like the execution is stuck in my async closure. – brian S Nov 07 '14 at 22:12
  • 1
    In the spirit of learning, could you explain why my comment is a bad idea? – Nate Cook Nov 07 '14 at 23:33
  • 1
    @NateCook To the contrary, I was trying to indicate that your comment contains the right recipe! Probably, should have written "As indicated by Nate, use another approach". Sorry for misunderstanding. – Anton Nov 09 '14 at 14:36
  • @brianS Sure, it does block the thread. This is exactly what you were asking in the first place. And as Nate suggests, just use a callback to update UI. – Anton Nov 09 '14 at 14:39
  • Yes I want to block the thread but dispatch_semaphore_signal(semaphore) doesn't unblock it. Is that expected? – brian S Nov 10 '14 at 15:37
  • @brianS It's expected in case the callback is called on the main thread. This is a typical deadlock problem: the main thread is waiting for unblock, the helper (sign in) thread completes and wants to perform the callback on the main thread that is waiting for unblock. – Anton Nov 10 '14 at 22:30
  • It's worth to mention that calling a completion handler (or a closure) on a _well known execution context_ (e.g. the main thread) is a rather poorly designed API as well. That's calling for deadlocks ;) Unless the API does provide a parameter for an execution context (e.g. a user supplied dispatch queue), it should execute a completion handler on a private execution context. With this measurement, deadlocks cannot occur - and it's also more efficient. – CouchDeveloper Nov 11 '14 at 19:59
  • ok thanks guys! Will try the approach with the completion handler – brian S Nov 11 '14 at 21:21
1

This is the solution in Swift 3. Again this blocks the thread until asynchronous task is complete, so it should be considered only in specific cases.

let semaphore = DispatchSemaphore(value: 0)

performAsyncTask {
    semaphore.signal()
}

// Thread will wait here until async task closure is complete   
semaphore.wait(timeout: DispatchTime.distantFuture)
Legoless
  • 10,942
  • 7
  • 48
  • 68