Wait for Async Task to Finish Completion Block Before Returning in App Delegate

Wait for async task to finish completion block before returning in app delegate

I'm unsure why the didFinishLaunching return status is dependent upon the success of your completion handler as you're not apparently even considering launchOptions. I'd hate to see you put an synchronous call (or more accurately, use a semaphore to convert an asynchronous method into a synchronous one) here, as it will slow down the app and, if its slow enough, you risk being killed by the watch dog process.

Semaphores are one common technique for making an asynchronous process synchronous:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
__block BOOL successful;
SPRManagedDocument *document = [SPRManagedDocument sharedDocument];

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

[document prepareWithCompletionHandler:^(BOOL success) {
successful = success;
dispatch_semaphore_signal(semaphore);
}];

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

return successful;
}

But, upon further review of what prepareWithCompletionHandler is doing, it's apparently calling methods that dispatch their own completion blocks to the main queue, so any attempts to make this synchronous will deadlock.

So, use asynchronous patterns. If you want to initiate this in the didFinishLaunchingWithOptions, you can have it post a notification:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
__block BOOL successful;
SPRManagedDocument *document = [SPRManagedDocument sharedDocument];

[document prepareWithCompletionHandler:^(BOOL success) {
successful = success;
[[NSNotificationCenter defaultCenter] postNotificationName:kDocumentPrepared object:nil];
}];

return successful;
}

And you can then have your view controller addObserverForName to observe this notification.

Alternatively, you can move this code out of the app delegate and into that view controller, eliminating the need for the notification.

How to wait for a delegate function to finish before returning value from main function

I believe that what you looking for is dispatch_semaphore_t.

Semaphores allow you to lock a thread until a secondary action is performed. This way, you can postpone the return of the method until the delegate has returned (if you are operating on a secondary thread).

The problem with such an approach is that you will be locking the thread! So, if you are operating in the main thread, your app will become unresponsive.

I would recommend you to consider moving the response to a completion block, similar to:

-(void)capture:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject completion:(void (^)(NSDicitionary* ))completion {
self.completion = completion
...
}

And call the completion at the end:

- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(NSError *)error
{
Cam *camera = [[Cam alloc] init];
self.res = [camera extractDepthInfo:photo];
self.completion(self.res);
}

=== Edit: Swift Code ===

The code above would be translated to something like:

var completion: (([AnyHashable: Any]) -> Void)?

func capture(options: [AnyHashable: Any], resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock, completion: @escaping ([AnyHashable: Any]) -> Void) {
self.completion = completion
...
}

func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
let cam = Cam()
let result = cam.extractDepthInfo(photo)
self.completion?(result)
}

An important note here is that the completion needs to be marked as @escaping in the capture method, given that the object will be copied.

Wait for async task to finish if you know that it's called n times

Apple has uploaded a project called "LazyTableImages" available here
. So I wrapped it up and made some paradigms which should probably fit your use-case.

 - (void)startDownload
{
NSURLRequest *request = [NSURLRequest requestWithURL:self.venue.imageURL];

// create an session data task to obtain and download the app icon
_sessionTask = [[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

// in case we want to know the response status code
NSInteger HTTPStatusCode = [(NSHTTPURLResponse *)response statusCode];

if (error != nil)
{
if ([error code] == NSURLErrorAppTransportSecurityRequiresSecureConnection)
{
// if you get error NSURLErrorAppTransportSecurityRequiresSecureConnection (-1022),
// then your Info.plist has not been properly configured to match the target server.
//
abort();
}
}

[[NSOperationQueue mainQueue] addOperationWithBlock: ^{

// Set appIcon and clear temporary data/image
UIImage *image = [[UIImage alloc] initWithData:data];
if (HTTPStatusCode == 200) {
if (image.size.width != kAppIconSize || image.size.height != kAppIconSize)
{
CGSize itemSize = CGSizeMake(kAppIconSize, kAppIconSize);
UIGraphicsBeginImageContextWithOptions(itemSize, NO, 0.0f);
CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
[image drawInRect:imageRect];
self.venue.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
else
{
self.venue.image = image;
}
}
else {// If anything goes wrong we should use a placeholder image
self.venue.image = [UIImage imageNamed:@"Placeholder.png"];
}

// call our completion handler to tell our client that our icon is ready for display
if (self.completionHandler != nil)
{
self.completionHandler();
}
}];
}];

[self.sessionTask resume];
}

wait for two asynchronous completion functions to finish, before executing next line code

Generally you'd use a dispatch group, enter the group before each asynchronous method, leave the group upon completion of each asynchronous method, and then set up a group notification when all "enter" calls are matched by corresponding "leave" calls:

override func viewDidLoad() {
super.viewDidLoad()

let group = dispatch_group_create()

dispatch_group_enter(group)
Females_NonChat() {
dispatch_group_leave(group)
}

dispatch_group_enter(group)
males_NonChat() {
dispatch_group_leave(group)
}

dispatch_group_notify(group, dispatch_get_main_queue()) {
print("finished executing both asynchronous functions")
}
}

func Females_NonChat(completionHandler: () -> ()) {
Anon_Ref.child("Chatting").child("female").observeSingleEventOfType(.Value) { snapshot in
if let FemInChatting = snapshot.value as? [String : String] {
print("executing")
}
completionHandler()
}
}

func males_NonChat(completionHandler: () -> ()) {
Anon_Ref.child("Chatting").child("male").observeSingleEventOfType(.Value) { snapshot in
print("executing")
completionHandler()
}
}

Ensuring that loop in async operation is completed before returning

You should use a completion handler for this.

func modifyGadgets(_ callback: @escaping ([Gadget]) -> ()) {
var gadgetsToModify = [Gadget]()

DispatchQueue.global(qos: .userInitiated).async {
for gadget in gadgetsToModify {
modifyThisGadget(gadget)
}

DispatchQueue.main.async {
callback(gadgetsToModify)
}
}
}

You can use it this way:

modifyGadgets { gadgets in
print(gadgets)
}


Related Topics



Leave a reply



Submit