I have a custom function for capturing true depth camera information and the function gets returned before the delegate functions have finished processing the captured photo. I need to somehow wait until the delegates have all completed before I return the correct value.
I tried wrapping the main function call into a synchronized block, but that did not solve the problem.
- (NSDictionary *)capture:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject
{
if (@available(iOS 11.1, *)) {
// Set photosettings to capture depth data
AVCapturePhotoSettings *photoSettings = [AVCapturePhotoSettings photoSettingsWithFormat:@{AVVideoCodecKey : AVVideoCodecJPEG}];
photoSettings.depthDataDeliveryEnabled = true;
photoSettings.depthDataFiltered = false;
@synchronized(self) {
[self.photoOutput capturePhotoWithSettings:photoSettings delegate:self];
}
}
// Somehow need to wait here until the delegate functions finish before returning
return self.res;
}
The delegate function which gets called too late:
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(NSError *)error
{
Cam *camera = [[Cam alloc] init];
self.res = [camera extractDepthInfo:photo];
}
Currently nil
is returned before the delegate gets ever called and only afterwards does the delegate function assign the desired result to self.res
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.