dpogue commented on code in PR #942:
URL: 
https://github.com/apache/cordova-plugin-camera/pull/942#discussion_r2722377374


##########
src/ios/CDVCamera.m:
##########
@@ -362,20 +347,35 @@ - (void)picker:(PHPickerViewController*)picker 
didFinishPicking:(NSArray<PHPicke
         
         // Check if it's a video
         if ([pickerResult.itemProvider 
hasItemConformingToTypeIdentifier:UTTypeMovie.identifier]) {
-            [pickerResult.itemProvider 
loadFileRepresentationForTypeIdentifier:UTTypeMovie.identifier 
completionHandler:^(NSURL * _Nullable url, NSError * _Nullable error) {
+            // Writes a copy of the data to a temporary file. This file will 
be deleted
+            // when the completion handler returns. The program should copy or 
move the file within the completion handler.
+            [pickerResult.itemProvider 
loadFileRepresentationForTypeIdentifier:UTTypeMovie.identifier
+                                                             
completionHandler:^(NSURL * _Nullable url, NSError * _Nullable error) {
                 if (error) {
-                    CDVPluginResult* result = [CDVPluginResult 
resultWithStatus:CDVCommandStatus_ERROR messageAsString:[error 
localizedDescription]];
+                    NSLog(@"CDVCamera: Failed to load video: %@", [error 
localizedDescription]);
+                    CDVPluginResult* result = [CDVPluginResult 
resultWithStatus:CDVCommandStatus_IO_EXCEPTION
+                                                                
messageAsString:[NSString stringWithFormat:@"Failed to load video: %@", [error 
localizedDescription]]];
                     [weakSelf.commandDelegate sendPluginResult:result 
callbackId:callbackId];
                     weakSelf.hasPendingOperation = NO;
                     return;
                 }
                 
-                dispatch_async(dispatch_get_main_queue(), ^{
-                    NSString* videoPath = [weakSelf createTmpVideo:[url path]];
-                    CDVPluginResult* result = [CDVPluginResult 
resultWithStatus:CDVCommandStatus_OK messageAsString:videoPath];
-                    [weakSelf.commandDelegate sendPluginResult:result 
callbackId:callbackId];
-                    weakSelf.hasPendingOperation = NO;
-                });
+                // The temporary file provided by PHPickerViewController is 
deleted when the completion
+                // handler exits. The file has to be copied in this thread, 
otherwise it will be gone.
+                NSString* videoPath = [weakSelf createTmpVideo:[url path]];
+                
+                // Send Cordova plugin result back, which must be done on the 
main thread

Review Comment:
   I think this comment is still wrong. The part about "must be done on the 
main thread" is not true, and we removed the code that was forcing it happened 
on the main thread (because it's not necessary)



##########
src/ios/CDVCamera.m:
##########
@@ -394,46 +393,40 @@ - (void)picker:(PHPickerViewController*)picker 
didFinishPicking:(NSArray<PHPicke
                 NSString *assetIdentifier = pickerResult.assetIdentifier;
                 
                 dispatch_async(dispatch_get_main_queue(), ^{
-                    [weakSelf processPHPickerImage:image 
assetIdentifier:assetIdentifier callbackId:callbackId options:pictureOptions];
+                    
+                    // Fetch metadata if asset identifier is available
+                    if (assetIdentifier) {
+                        PHFetchResult *result = [PHAsset 
fetchAssetsWithLocalIdentifiers:@[assetIdentifier] options:nil];
+                        PHAsset *asset = result.firstObject;
+                        
+                        if (asset) {
+                            PHImageRequestOptions *imageOptions = 
[[PHImageRequestOptions alloc] init];
+                            imageOptions.synchronous = YES;
+                            imageOptions.networkAccessAllowed = YES;
+                            
+                            [[PHImageManager defaultManager] 
requestImageDataAndOrientationForAsset:asset
+                                                                               
             options:imageOptions
+                                                                               
       resultHandler:^(NSData *_Nullable imageData, NSString *_Nullable 
dataUTI, CGImagePropertyOrientation orientation, NSDictionary *_Nullable info) {
+                                NSDictionary *metadata = imageData ? [weakSelf 
convertImageMetadata:imageData] : nil;
+                                dispatch_async(dispatch_get_main_queue(), ^{
+                                    [weakSelf finalizePHPickerImage:image
+                                                           metadata:metadata
+                                                         callbackId:callbackId
+                                                            
options:pictureOptions];
+                                });
+                            }];
+                            return;
+                        }
+                    }

Review Comment:
   Does all of this work need to happen on the main thread, or just the call to 
`finalizePHPickerImage` below?



##########
src/ios/CDVCamera.m:
##########
@@ -865,21 +804,116 @@ - (void)resultForImage:(CDVPictureOptions*)options
 - (CDVPluginResult*)resultForVideo:(NSDictionary*)info
 {
     NSString* moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] 
absoluteString];
+    
     // On iOS 13 the movie path becomes inaccessible, create and return a copy
     if (IsAtLeastiOSVersion(@"13.0")) {

Review Comment:
   We should replace this with a `if (@available(iOS 13.0, *))` check, rather 
than `IsAtLeastiOSVersion` (which is deprecated)



##########
src/ios/CDVCamera.m:
##########
@@ -865,21 +804,116 @@ - (void)resultForImage:(CDVPictureOptions*)options
 - (CDVPluginResult*)resultForVideo:(NSDictionary*)info
 {
     NSString* moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] 
absoluteString];
+    
     // On iOS 13 the movie path becomes inaccessible, create and return a copy
     if (IsAtLeastiOSVersion(@"13.0")) {
-        moviePath = [self createTmpVideo:[[info 
objectForKey:UIImagePickerControllerMediaURL] path]];
+        moviePath = [self copyFileToTemp:[[info 
objectForKey:UIImagePickerControllerMediaURL] path]];
     }
+    
     return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK 
messageAsString:moviePath];
 }
 
-- (NSString*)createTmpVideo:(NSString*)moviePath
+/**
+ Generates a unique temporary file path for a file extension.
+ 
+ The filename is prefixed with `cdv_photo_` and suffixed with the provided
+ file extension. A UNIX timestamp (seconds since 1970) is used to ensure
+ uniqueness between calls.
+ 
+ Threading: Safe to call from any thread. Uses NSTemporaryDirectory() and
+ does not perform any I/O; it only constructs a path string.
+ 
+ @param fileExtension  The desired file extension without a leading dot
+                      (for example, "jpg", "png", or the original video
+                      extension like "mov").
+ 
+ @return An absolute path string within the app's temporary directory,
+         e.g. 
`/var/mobile/Containers/Data/Application/<UUID>/tmp/cdv_photo_<timestamp>.jpg`.
+ 
+ @discussion The returned path is not created on disk. Callers are responsible
+             for writing data to the path and handling any errors.
+ 
+ @note Only files whose names start with `cdv_photo_` are cleaned up by the
+       plugin's `cleanup:` method.
+ **/
+- (NSString*)tempFilePathForExtension:(NSString*)fileExtension
+{
+    // Return a unique file name like
+    // 
`/var/mobile/Containers/Data/Application/<UUID>/tmp/cdv_photo_<timestamp>.jpg`.
+    return [NSString stringWithFormat:
+            @"%@/%@%lld.%@",
+            [NSTemporaryDirectory() stringByStandardizingPath],
+            CDV_PHOTO_PREFIX,
+            (long long)[[NSDate date] timeIntervalSince1970],
+            fileExtension];
+}
+
+- (NSString*)copyFileToTemp:(NSString*)filePath
+{
+    NSFileManager* fileManager = [[NSFileManager alloc] init];
+    NSString* tempFilePath = [self tempFilePathForExtension:[filePath 
pathExtension]];
+    NSError *error = nil;
+    
+    // Copy file to temp directory
+    BOOL copySuccess = [fileManager copyItemAtPath:filePath 
toPath:tempFilePath error:&error];
+    
+    if (!copySuccess || error) {
+        NSLog(@"CDVCamera: Failed to copy file from %@ to temporary path %@. 
Error: %@", filePath, tempFilePath, [error localizedDescription]);
+        return nil;
+    }
+    
+    // Verify the copied file exists
+    if (![fileManager fileExistsAtPath:tempFilePath]) {
+        NSLog(@"CDVCamera: Copied file does not exist at temporary path: %@", 
tempFilePath);
+        return nil;
+    }
+    
+    return [[NSURL fileURLWithPath:tempFilePath] absoluteString];
+}
+
+/**
+  Called by JS camera.cleanup()
+  Removes intermediate image files that are kept in temporary storage after
+  calling camera.getPicture.
+*/
+- (void)cleanup:(CDVInvokedUrlCommand*)command
 {
-    NSString* moviePathExtension = [moviePath pathExtension];
-    NSString* copyMoviePath = [self tempFilePath:moviePathExtension];
+    // empty the tmp directory
     NSFileManager* fileMgr = [[NSFileManager alloc] init];
-    NSError *error;
-    [fileMgr copyItemAtPath:moviePath toPath:copyMoviePath error:&error];
-    return [[NSURL fileURLWithPath:copyMoviePath] absoluteString];
+    NSError* err = nil;
+    BOOL hasErrors = NO;
+
+    // clear contents of NSTemporaryDirectory

Review Comment:
   Would be better if this comment mentioned that it only clears contents that 
match the prefix



##########
src/ios/CDVCamera.m:
##########
@@ -865,21 +804,116 @@ - (void)resultForImage:(CDVPictureOptions*)options
 - (CDVPluginResult*)resultForVideo:(NSDictionary*)info
 {
     NSString* moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] 
absoluteString];
+    
     // On iOS 13 the movie path becomes inaccessible, create and return a copy
     if (IsAtLeastiOSVersion(@"13.0")) {
-        moviePath = [self createTmpVideo:[[info 
objectForKey:UIImagePickerControllerMediaURL] path]];
+        moviePath = [self copyFileToTemp:[[info 
objectForKey:UIImagePickerControllerMediaURL] path]];
     }
+    
     return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK 
messageAsString:moviePath];
 }
 
-- (NSString*)createTmpVideo:(NSString*)moviePath
+/**
+ Generates a unique temporary file path for a file extension.
+ 
+ The filename is prefixed with `cdv_photo_` and suffixed with the provided
+ file extension. A UNIX timestamp (seconds since 1970) is used to ensure

Review Comment:
   Is seconds granular enough to ensure uniqueness? Not sure if we support 
selecting multiple files, but there's a chance those could generate filenames 
that collide?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to