UIImagePickerController - Save Recorded Video to Camera Roll Error - ios

I'm trying to save a video recorded with camera using the UIImagePickerController. I've tried two different ways, and both are not working at all.
First attempt:
if let pathURL = info["UIImagePickerControllerMediaURL"] as! URL?, mediaType == kUTTypeMovie {
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: pathURL)
}, completionHandler: { (isSuccessfull, error) in
if error != nil {
// ERROR
// error.localizedDescription value below
// "The operation couldn’t be completed. (Cocoa error -1.)"
} else if isSuccessfull {
// SUCCESS
}
})
}
On that attempt, I'm always getting the error "The operation couldn’t be completed. (Cocoa error -1.)"
Second attempt:
if let filePath = info["UIImagePickerControllerMediaURL"] as! NSURL,
let stringPath = filePath.path {
UISaveVideoAtPathToSavedPhotosAlbum(stringPath, self, #selector(Controller.videoSaved(videoPath:didFinishSavingWithError:contextInfo:)), nil)
}
This is not working either... The callback, the error is nil, but the video is not saved into the camera roll. Also, when I use the UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(stringPath), this method also returning me false all the time. I'm not able to understand why the video recorded would be incompatible to save.
I really don't know what I'm doing wrong. What am I missing? A configuration in plist?
Here's how look like the value of info["UIImagePickerControllerMediaURL"]:
file:///private/var/mobile/Containers/Data/Application/2B9BE04A-17B3-49CE-B4BA-C45F183E9A64/tmp/837294779101__C4825AFE-8140-420F-ACD0-64623C7A4753.MOV
For pictures taken with camera, PHPhotoLibrary method works very fine... It's only videos that is not able to be saved.

Not sure in 100% but I believe that it wasn't working for me because I was saving the video only after a request to my API was successful. My assumption is that the path URL is temporary and has a very short time of life. When I pass it to another controller, and make API call, it doesn't work. But if I save the video directly in the delegate method of UIImagePickerViewController, then it works fine.

Related

QLThumbnailGenerator starts failing when called multiple times (on actual device) iOS 13

I am trying to create thumbnail images of multiple Wallet Passes (.pkpass) by running a loop over all (around 200) passes in a specific folder and calling generateBestRepresentation(for:) for each of them.
This is the code:
let passesDirURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("Passes")
let size = CGSize(width: 1600, height: 1600)
let scale = UIScreen.main.scale
if let passURLs = try? FileManager.default.contentsOfDirectory(
at: self.passesDirURL,
includingPropertiesForKeys: nil,
options: .skipsHiddenFiles
),
!passURLs.isEmpty {
for passURL in passURLs {
// Create the thumbnail request.
let request = QLThumbnailGenerator.Request(
fileAt: passURL,
size: size,
scale: scale,
representationTypes: .thumbnail
)
// Retrieve the singleton instance of the thumbnail generator and generate the thumbnails.
let generator = QLThumbnailGenerator.shared
generator.generateBestRepresentation(for: request) { thumbnail, error in
if let error = error as? QLThumbnailError {
print ("Thumbnail generation error: \(error)")
print ("Thumbnail generation localizedDescription: \(error.localizedDescription)")
print ("Thumbnail generation errorUserInfo: \(error.errorUserInfo)")
print ("Thumbnail generation errorCode: \(error.errorCode)")
} else {
print ("Thumbnail generation OK")
//do something with thumbnail here
}
}
}
}
This works fine on the simulator, but on an actual device (iPhone Xs Max) sooner or later I start getting errors and the thumbnail generation fails for a big fraction of the passes. The output looks as follows:
Thumbnail generation error: related decl 'e' for QLThumbnailError(_nsError: Error Domain=QLThumbnailErrorDomain Code=3 "No thumbnail in the cloud for file:///private/var/mobile/Containers/Data/Application/DCF703F7-9A1A-4340-86EB-42579D678EEF/Documents/Passes/pass123.pkpass" UserInfo={NSErrorFailingURLKey=file:///private/var/mobile/Containers/Data/Application/DCF703F7-9A1A-4340-86EB-42579D678EEF/Documents/Passes/pass123.pkpass})
Thumbnail generation localizedDescription: The operation couldn’t be completed. (QLThumbnailErrorDomain error 3.)
Thumbnail generation errorUserInfo: ["NSErrorFailingURLKey": file:///private/var/mobile/Containers/Data/Application/DCF703F7-9A1A-4340-86EB-42579D678EEF/Documents/Passes/pass123.pkpass]
Thumbnail generation errorCode: 3
The error description sounds confusing ("No thumbnail in the cloud for file") as these are not iCloud files.
As the error does not occur when calling the thumbnail generation individually, this seems to be some memory/performance issue. I tried to workaround in many ways, including using a semaphore in the for loop waiting for the completion of one call of generateBestRepresentation to start the next call, which reduced but not eliminated the issue. The only way it worked without error was adding a very long sleep (5 seconds) after the semaphore.wait() statement, but this is no acceptable solution.
Another way I tried was using saveBestRepresentation (as suggested in Apple's documentation), but this did not solve the issue.
Has anyone faced a similar issue and was able to find an acceptable solution?

Presentation is in process issue following Error

I am playing with Vision for text recognition, I present the camera, take a photo and the text is detected and processed...running very well. The issue I have is when there is no text in the photo, I get an error from VNImageRequestHandler, which is fine, but the issue is that I can't re-open the camera, I get "Warning: Attempt to present UIImagePickerController: ... while a presentation is in progress!.
here is some code where I process the image looking for some text...
guard let image = image, let cgImage = image.cgImage else { return }
let requests = [textDetectionRequest]
let imageRequestHandler = VNImageRequestHandler(cgImage: cgImage, orientation: .up, options: [:] )
DispatchQueue.global(qos: .userInitiated).async {
do {
try imageRequestHandler.perform(requests)
} catch let error {
print("Error: \(error)")
}
}
}
the Error is
"Error: Error Domain=com.apple.vis Code=11 "encountered unknown
exception" UserInfo={NSLocalizedDescription=encountered unknown
exception}"
which is fine, I just want to be able to open the UIImagePickerController after that Error.
I have tried to dismiss the UIImagePickerController, does not work... and I can't find what presentation is really in process.
Thanks.
For me it was something completely unrelated that caused this error. After the VNRequestCompletionHandler was called, I attempted to initialize a malformed NSPredicate. Then I would get the error that you described. Fixing the predicate also fixed the issue you described.
I would look to see if there's any work you're doing after the completion handler is called that can throw an error and fix that.

PHAsset copy video completion

I wish to copy video file from Photo Library to my app's Documents directory and wish to be notified about completion. Here is what I do:
let videoAsset = fetchResult.object(at: indexPath.item)
print(videoAsset.description)
let options = PHVideoRequestOptions()
options.version = .original
PHImageManager.default().requestAVAsset(forVideo: videoAsset, options: options) { [weak self] (avAsset, audioMix, info) in
if let avurlAsset = avAsset as? AVURLAsset {
let url = avurlAsset.url
let toUrl = //some Url
let fileManager = FileManager.default
do {
try fileManager.copyItem(at: url, to: toUrl)
} catch {
NSLog("Unable to copy file from \(url) to \(toUrl)")
}
}
}
Only problem with this approach is I have no way to be notified of completion of copyItem. What is the alternative to copyItem method (or altogether a different approach to above) that is atleast blocking till copy finishes? Is it possible to use FileHandle & read consecutive bytes and write to another file? Will that be synchronous enough?
EDIT: As pointed by Alex, copyItem is actually synchronous routine. On closer inspection, I see I sometimes get errors on copying. Not sure why the permission errors show up when it is app's Documents folder where I copy.
2018-08-27 20:30:07.485841+0530 MyProject[3577:1288452] Copying file...
2018-08-27 20:30:07.487880+0530 MyProject[3577:1288452] stat on /var/mobile/Media/DCIM/107APPLE/IMG_7915.MP4: Operation not permitted
2018-08-27 20:30:07.512994+0530
MyProject[3577:1288452] Unable to copy file from file:///var/mobile/Media/DCIM/107APPLE/IMG_7915.MP4 to file:///var/mobile/Containers/Data/Application/CC13FD5A-E4CF-42A1-931F-2F1FFE799C15/Documents/IMG-0027.mov, Error Domain=NSCocoaErrorDomain Code=513 "“IMG_7915.MP4” couldn’t be copied because you don’t have permission to access “Documents”." UserInfo=
{NSSourceFilePathErrorKey=/var/mobile/Media/DCIM/107APPLE/IMG_7915.MP4, NSUserStringVariant=(
Copy
),
NSDestinationFilePath=/var/mobile/Containers/Data/Application/CC13FD5A-E4CF-42A1-931F-2F1FFE799C15/Documents/IMG-0027.mov, NSFilePath=/var/mobile/Media/DCIM/107APPLE/IMG_7915.MP4, NSUnderlyingError=0x111c441c0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
Copyitem: Copies the item at the specified path to a new location synchronously.
Returns true if the item was copied successfully or the file manager’s delegate stopped the operation deliberately. Returns false if an error occurred.
This is a sync method so after it executed after catch without error then it means successful copied.
https://developer.apple.com/documentation/foundation/filemanager/1407903-copyitem

getting error when i trying to convert AVAssetURL to Data

Error Domain=NSCocoaErrorDomain Code=257 "The file “IMG_9807.MOV” couldn’t be opened because you don’t have permission to view it." UserInfo={NSFilePath=/var/mobile/Media/DCIM/109APPLE/IMG_9807.MOV, NSUnderlyingError=0x1c1e5fe00 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"
i am sending asset URL to other controller and try to convert into data
PHImageManager.default().requestAVAsset(forVideo: self.albumView.phAsset, options: options) { (video, audioMix, info) in
DispatchQueue.main.async {
let urlAsset = video as! AVURLAsset
self.dismiss(animated: false, completion: {
self.delegate?.fusumaVideoCompleted(withFileURL: urlAsset.url)
})
}
}
here below methods for convert AVAssetUrl to data
do {
let data = try Data(contentsOf: product.videoURL, options: .mappedIfSafe)
return .upload(.multipart([MultipartFormData(provider: .data(data), name: "post[video]", fileName: "video.\(pathExtension)", mimeType: "video/\(pathExtension)")]))
} catch {
debugPrint(error)
}
As the error tells you, you cannot access the video file in the user's photo library by way of its URL for purposes of uploading it. You should obtain the video data and upload that. A video is very big, so you should not get the data directly and hold it in memory; instead, export the data to a file in a place that you are allowed to access, such as the Temporary folder.
To do that, you might (for example) use this method:
https://developer.apple.com/documentation/photos/phassetresourcemanager/1616280-writedata
Or this one:
https://developer.apple.com/documentation/photos/phimagemanager/1616981-requestexportsession
If you use the Mail app to email a video from your own device's photo library, you will actually see that happening; there is a pause with a progress bar while the video is exported, and then the email is constructed.

Export file from iOS app in swift

I'm fairly new to Swift and iOS development in general. My app has a model that can easily be expressed as comma separated values (csv), so naturally I want the user to be able to export the data as an csv file and opening that file in another application. Since I didn't find any examples in Swift, I gave it a try on my own:
func ExportToCSV(delegate: UIDocumentInteractionControllerDelegate){
let fileName = NSTemporaryDirectory().stringByAppendingPathComponent("myFile.csv")
let url: NSURL! = NSURL(fileURLWithPath: fileName)
var data = "Date,Time,Count\n2014-11-21,14.00,42"
data.writeToURL(url, atomically: true, encoding: NSUTF8StringEncoding, error: nil)
if url != nil {
let docController = UIDocumentInteractionController(URL: url)
docController.UTI = "public.comma-separated-values-text"
docController.delegate = delegate
docController.presentPreviewAnimated(true)
}
}
(the delegate parameter is the view that calls the function, as in MyClass.ExportToCSV(self))
This works, mostly, and I see the following views:
However, in the Simulator I get the following warning:
Unbalanced calls to begin/end appearance transitions for <QLRemotePreviewContentController: 0x7fcd720da800>.
as well as
Unknown activity items supplied: ("<QLPrintPageRenderer: 0x7fcd73861ee0>","<UIPrintInfo: 0x7fcd714b9030>")
when I click the action button, then after a while
Communications error: <OS_xpc_error: <error: 0x10e032b10> {
count = 1, contents = "XPCErrorDescription"
=> <string: 0x10e032f18> { length = 22, contents = "Connection interrupted" }
}>
and when I click Mail there is a crash with the following error:
viewServiceDidTerminateWithError: Error Domain=_UIViewServiceInterfaceErrorDomain
Code=3 "The operation couldn’t be completed. (_UIViewServiceInterfaceErrorDomain error 3.)"
UserInfo=0x7fcd71631460 {Message=Service Connection Interrupted}
<MFMailComposeRemoteViewController: 0x7fcd73864aa0> timed out waiting for fence
barrier from com.apple.MailCompositionService
Although on the actual device everything works as planned, so many errors throw me off a bit. Is there a better solution?
This is probably due to objects already have been deallocated when they are accessed by the mail or printing app. Try to declare your docController and url variables as class properties so they keep alive as long as the view controller exists.

Resources