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.
Related
My Objective-C iOS app schedules a local notification that has a userInfo dictionary and one small JPEG image attachment.
The image is attached like this:
content.attachments = #[[UNNotificationAttachment attachmentWithIdentifier:myIdentifier
URL:[imageURL filePathURL]
options:#{UNNotificationAttachmentOptionsTypeHintKey : UTTypeJPEG}
error:&error]];
This works fine. The notification is correctly scheduled.
If I ignore the Watch and let the notification appear on my phone's Lock Screen, the image is there.
Going back to the Watch. The Watch app receives the notification, and the didReceive method is called in the NotificationController.
No matter what I try, I can't get the image from the notification.
I've tried converting the image to NSData and adding it to the userInfo dictionary, but the data is too large despite the image actually being only a few Kb.
NotificationController.swift: (image is an Image type, and is sent to the NotificationView to use as the background.)
guard let attachment = notification.request.content.attachments.first
else {
print("Couldn't get the first attachment, using default image")
image = Image.init(kDefaultImage)
return
}
// We get here, so we know there's an attachment
if attachment.url.startAccessingSecurityScopedResource() {
let imageData = try? Data.init(contentsOf: attachment.url)
if let imageData = imageData {
image = Image(uiImage: UIImage(data: imageData) ??
UIImage.init(imageLiteralResourceName: kDefaultImageMasked))
}
attachment.url.stopAccessingSecurityScopedResource()
} else {
print("Couldn't access the file, using default")
image = Image.init(kDefaultImageMasked)
}
I always get told I can't access the file in the security scoped bit.
If I take out that check I get Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value because the code doesn't put anything into image.
Obviously I would put the default image in there instead of crashing, but once the Watch app crashes the notification that's shown on the Watch shows the correct image, but it's obviously not using my NotificationView because that crashed. It's using the standard mirror feature to show the notification, so it's completely bypassing my code at that point.
I've also tried this:
if(attachment.url.isFileURL) {
print("attachment image is a file url")
}
It does print out that it's a file url, and it's: file:///var/mobile/Library/BulletinDistributor/Attachments/com.mycompany.myapp/<UUID>.jpeg.
But this fails, so the file doesn't exist at that path, or I'm not allowed to load files from there (?):
if(FileManager.default.fileExists(atPath: attachment.url.path)) {
...
How do I get the image from the attachment? I've tried getting the image as Data, as an Image, as a UIImage. Nothing works.
This was simple to do in the old Objective-C WatchKit extension stuff:
NSArray *attachments = notification.request.content.attachments;
if(attachments.count == 1) {
[_backgroundGroup setBackgroundImage:attachments[0]];
}
No problems there; it always worked.
Any ideas?
Before I updated to iOS 14 on my iPhone, this code was working perfectly. After, iOS 14 this is weirdly not running... it is very odd and I have not seen any solution online, additionally from my investigation, I have not been able to see any change.
This code is used in order to retrieve a videoURL for this video from the imported Camera Roll (I use import Photos...).
phResourceManager.writeData(for: resource.last!, toFile: newURL!, options: resourceRequestOptions) { (error) in
if error != nil {
print(error, "not c67omplted error?")
} else {
print("woah completedd 345?")
newUserTakenVideo.videoURL = newURL
print(newUserTakenVideo.videoURL, "<--?")
}
}
EDIT:
To be clear, it "does not run" means the compleition block never runs... as in it never even runs and gives an error, the compleition block simply never is called (nothing prints at least..)
And here is a print statement printing out all the values I pass in to the parameters:
phResourceManager:
<PHAssetResourceManager: 0x282d352c0>
resource.last:
Optional(<PHAssetResource: 0x28128bc00> {
type: video
uti: public.mpeg-4
filename: v07044090000bu6n1nhlp4leque7r720.mp4
asset: C97B45D3-7039-4626-BA3E-BCA67912A2A9/L0/001
locallyAvailable: YES
fileURL: file:///var/mobile/Media/DCIM/113APPLE/IMG_3404.MP4
width: 576
height: 1024
fileSize: 4664955
analysisType: unavailable
cplResourceType: Original
isCurrent: YES
})
newURL:
Optional(file:///var/mobile/Containers/Data/Application/E2792F47-142E-4601-8D5B-F549D03C9AFE/Documents/Untitled%2027228354.MP4)
resourceRequestOptions:
<PHAssetResourceRequestOptions: 0x28230d480>
Note: this is the decleration for the resource variable:
let resource = PHAssetResource.assetResources(for: (cell?.assetPH)!)
I have a solution to this! Swift 4+, tested on iOS 14!
I looked through using a PHAssetResourceRequest, but the file names were messed with in the process, and it generally didn't work with my sandbox. Then I also tried requesting a AVPlayerItem from the PHAsset but this too, did not work with sandboxing...
But then, I tried simply using PHAssetResourceManager.default().writeData(... and seemingly started working!
I tested a bit more and seemed to work, here is the full code:
let resource = PHAssetResource.assetResources(for: (cell?.assetPH)!)
let resourceRequestOptions = PHAssetResourceRequestOptions()
let newURL = ExistingMediaVC.newFileUrl
PHAssetResourceManager.default().writeData(for: resource.last!, toFile: newURL!, options: resourceRequestOptions) { (error) in
if error != nil {
print(error, "error")
} else {
print("good")
newUserTakenVideo.videoURL = newURL
}
}
It is quite simple!! Tell me if anything is not working, and note I still use the ExisitingMedia.fileURL variable you used in your original code as well :)
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?
Full error message is:
[main] *** Error from FPBookmarkableStringFromDocumentURL, file:///private/var/mobile/Library/Mobile%20Documents/com~apple~CloudDocs/MY_APP/hello.txt -> Error Domain=NSCocoaErrorDomain Code=257 "The file couldn’t be opened because you don’t have permission to view it."
This error is thrown from a SceneDelegate (introduced in iOS 13) when I relaunch my app & attempt to reopen previously opened file (for example when the app was "backgrounded" & the memory freed, so I want to reload the state of the scene).
I followed the steps from this WWDC session https://developer.apple.com/videos/play/wwdc2019/212 & the downloaded example source code from https://developer.apple.com/documentation/uikit/app_and_environment/scenes/supporting_multiple_windows_on_ipad
I basically copied their configure(window: UIWindow?, with activity: NSUserActivity) -> Bool function and transformed it to fit my needs. It seems to work, flawlessly, but I'm getting the error.
I'm using the default NSUserActivity technique described in "Adopting Handoff in Document-Based Apps" section of https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/Handoff/AdoptingHandoff/AdoptingHandoff.html#//apple_ref/doc/uid/TP40014338-CH2-SW17
Here's the body of the configure function:
if activity.activityType == "com.myName.MyApp.openedDocumentUserActivity" {
if let documentURL = activity.userInfo?[UIDocument.userActivityURLKey] as? URL {
if let docBrowserVC = window?.rootViewController as? DocumentBrowserViewController {
documentURL.startAccessingSecurityScopedResource() // tried this, it returns false
docBrowserVC.presentDocument(at: documentURL)
return true
}
}
}
return false
If anyone knows about a workaround, thank you.
I had to create bookmark data from the URL and persist that in the NSUserActivity. Using UIDocument.documentURL is not sufficient because it is security-scoped.
For creating the bookmark (no error checking in this snippet):
let userActivity = NSUserActivity(activityType: "com.foobar")
let bookmarkData = try? documentURL.bookmarkData()
userActivity.addUserInfoEntries(from: ["URLBookmarkData": bookmarkData])
scene.userActivity = userActivity
Then for reading:
let bookmarkData = userActivity.userInfo?["URLBookmarkData"] as? Data {
let resolvedURL = try? URL(resolvingBookmarkData: bookmarkData, options: NSURL.BookmarkResolutionOptions(), relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale)
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.